OUCC

BLOG

記事一覧 タグ一覧

C言語でコンパイルが通らなかった話

投稿日:

この記事は、OUCC Advent Calendar 2023の19日目の記事です。


私は情報科学を学び始めた大学1年です。 先日、課題で作成したC言語のコードを提出した際、コンパイルが通らないと言われました。 自身の環境では問題なくコンパイルできていたので、原因が分からず困りました。

この記事では、その原因について分かったことを書いてみます。

間違いなどに気づいた場合、教えていただけると幸いです。

コンパイルできなかった原因

原因は、strcat_c()を使用していたことでした。

とほほのC言語入門 - とほほのWWW入門によると、

gcc では strcpy_s() や strcat_s() をサポートしていません。

とのことでした。

また、ISO/IEC 9899:2011の583ページに、次のような記述がありました。

This annex specifies a series of optional extensions that can be useful in the mitigation of security vulnerabilities in programs, and comprise new functions, macros, and types declared or defined in existing standard headers.

以下、邦訳です。

この附属書は、プログラムのセキュリティ脆弱性を緩和するのに有用な一連のオプショナル拡張を規定し、既存の標準ヘッダーで宣言または定義された新しい関数、マクロ、型から構成される。


確認すると、課題をチェックする環境がgcc であったようで、コンパイルできなかったようです。

自分の環境でコンパイルできた理由

課題のチェック時にコンパイルができなかった原因はわかりましたが、自身の環境でコンパイル出来た理由を知りたくなりました。

私の環境は以下の通りです。

$ gcc --version
gcc (Rev6, Built by MSYS2 project) 13.1.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

ネットで調べてみましたが、調べ方が悪いのか、特に情報を得られませんでした。

そこで、実行時にヘッダファイルを探しに行っている、ファイルの中身を確認してみました。 その中のstring.hにはやはり、strcat_s()は定義されていませんでした。 しかし、string_s.hと言うファイルの中にstrcat_s()を見つけました。

_CRTIMP errno_t __cdecl strcat_s(char *_Dst, rsize_t _SizeInBytes, const char * _Src);

これが、読み込まれているのでは、と思い、次のようなmain.cを書いてコンパイルしようとしてみました。

#include <stdio.h>
#include <string.h>

errno_t strcat_s(char *_Dst, rsize_t _SizeInBytes, const char * _Src);

int main()
{
    char s1[12] = "";
    char s2[5] = "abcd";
    strcat_s(s1, 12, s2);

    return 0;
}

errno_t strcat_s(char *_Dst, rsize_t _SizeInBytes, const char * _Src)
{
    printf("called\n");
    return 0;
}

コンパイルしようとしてみます。

$ gcc main.c
main.c:4:9: warning: 'strcat_s' redeclared without dllimport attribute: previous dllimport ignored [-Wattributes]
    4 | errno_t strcat_s(char *_Dst, rsize_t _SizeInBytes, const char * _Src);
      |

2重定義で警告が出ました。

次に、main.cstrcat_sの第2引数の型をint型に変更してコンパイルしようとしてみました。

#include <stdio.h>
#include <string.h>

errno_t strcat_s(char *_Dst, int _SizeInBytes, const char * _Src);

int main()
{
    char s1[12] = "123";
    char s2[5] = "abcd";

    strcat_s(s1, 12, s2);

    return 0;
}

errno_t strcat_s(char *_Dst, int _SizeInBytes, const char * _Src)
{
    printf("called\n");
    return 0;
}

コンパイルしようとしてみます。

$ gcc main.c
main.c:4:9: error: conflicting types for 'strcat_s'; have 'errno_t(char *, int,  const char *)' {aka 'int(char *, int,  const char *)'}
    4 | errno_t strcat_s(char *_Dst, int _SizeInBytes, const char * _Src);
      |         ^~~~~~~~
In file included from C:/msys64/mingw64/include/string.h:194,
                 from main.c:2:
C:/msys64/mingw64/include/sec_api/string_s.h:53:27: note: previous declaration of 'strcat_s' with type 'errno_t(char *, rsize_t,  const char *)' {aka 'int(char *, long long unsigned int,  const char *)'}
   53 |   _CRTIMP errno_t __cdecl strcat_s(char *_Dst, rsize_t _SizeInBytes, const char * _Src);
      |                           ^~~~~~~~
main.c:16:9: error: conflicting types for 'strcat_s'; have 'errno_t(char *, int,  const char *)' {aka 'int(char *, int,  const char *)'}
   16 | errno_t strcat_s(char *_Dst, int _SizeInBytes, const char * _Src)
      |         ^~~~~~~~
C:/msys64/mingw64/include/sec_api/string_s.h:53:27: note: previous declaration of 'strcat_s' with type 'errno_t(char *, rsize_t,  const char *)' {aka 'int(char *, long long unsigned int,  const char *)'}
   53 |   _CRTIMP errno_t __cdecl strcat_s(char *_Dst, rsize_t _SizeInBytes, const char * _Src);
      |                           ^~~~~~~~

出力の4行目のC:/msys64/mingw64/include/string.hの194行目を見てみると、

#include <sec_api/string_s.h>

とありました。

include
├─sec_api
│  └─string_s.h
└─string.h

ファイル構成は上のようになっているので、#include <string.h>したときに、 string_s.hincludeされており、私の環境では、#include <string.h>だけで、strcat_s()が使えていたようでした。