86275
興味がありC言語について勉強中です。

strlen()関数がカウントできる（戻り値としてリターンできる）最大文字数はいくつになりますか？

戻り値の型は確か`size_t`
だったと思いますが、実行環境やコンパイラによって最大値が変わる場合、何を見れば/どこを調べれば、戻り値の最大文字数がいくつであると分かるでしょうか。

ちなみにmacOS 10.14.6 利用しており、プロセッサは `3.1 GHz Intel Core i7` と表示があるので 64bit
PCなのかなと思いますがこちらが関係しそうでしょうか（Core i7だったらどのような場合でも絶対に64bitになりますか？）

下記のようなサンプルコードで何文字までカウントできるか試してみましたが、840万文字付近でエラー = segmentation fault
となりました。`int` としている箇所の型をすべて `size_t`
としても同様です。最大値とそれを超える値の境目（しきい値）を実際に目で確認したかったのですが、もし他に良い検証方法があればそちらもご教授いただけたら嬉しいです。

```

    int main(void)
    {
        // int num = INT_MAX; // segmentation fault
        // int num = 8400000; // segmentation fault
        int num = 8350000; // OK
        char str[num];
        int i = 0;
        int j = num - 1;
    
        while (i < j) {
            str[i] = '1';
            i++;
        }
        str[i] = '\0';
        printf("%zu\n", strlen(str)); // 出力: 8349999
        return (0);
    }
    
```
CC BY-SA 4.0
2022-02-11T14:30:04.710
86274
2022-02-13T05:57:58.333
43186
question
c
C言語で strlen() 関数がカウントできる最大文字数はいくつでしょうか？
`strlen()`自体に制限はありません。戻り値は`size_t`なのでこの最大値まで可能です。  
あとはどれだけ長い文字列を用意できるかの問題であり、そこは`strlen()`とは無関係です。

> 840万文字付近でエラー = segmentation fault となりました。

>
```

>     int num = 8350000; // OK
>     char str[num];
>  
```

このコードはスタック領域上に変数を確保するものであり、スタックサイズを超えることはできません。840万文字付近が限界なら、それがその環境のスタックサイズの限界なのでしょう。`malloc()`等を使用するとスタック領域ではなくより広いヒープ領域上にメモリ確保できます。

* * *

> 最大値とそれを超える値の境目（しきい値）を実際に目で確認したかったのですが、もし他に良い検証方法があればそちらもご教授いただけたら嬉しいです。

`strlen()`のソースコードを読んでみるのはどうでしょうか？

  * glibc: <https://github.com/bminor/glibc/blob/master/string/strlen.c>
  * newlib: <https://github.com/bminor/newlib/blob/master/newlib/libc/string/strlen.c>

最適化されていて複雑ですが、newlibで最適化部分を排除すれば簡単に読めるかもしれません。

```

    size_t
    strlen (const char *str)
    {
      const char *start = str;
      while (*str)
        str++;
      return str - start;
    }
    
```

となっていて、文字列長に依存しない実装であることは簡単に読み取れるかと思います。

追記：
あくまでglibcやnewlibの実装をあげたものですが、macOSにおいても同様の実装がされているはずですので、必要であれば実際のソースコードを確認してください。

* * *

oririさんの回答はいろいろと問題があるので、指摘しておきます。

> macOS での C-Compilerは gccのようです

コメントにも書きましたが、実行ファイル名を取り上げることに意味はありません。gccという名前から[GNU Compiler
Collection](https://ja.wikipedia.org/wiki/GNU%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3)が連想されますが、実際には[Clang](https://ja.wikipedia.org/wiki/Clang)のはずです。ちなみにClangを作ったのはAppleです。

また、macOSの質問なのでLinuxのman pageを見ることに意味はありません。素直にmacOSのman
pageを見るべきです。[`strlen(3)`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/strlen.3.html)も用意されています。

> OSというか CPUが 32bitモードか 64bitモード(で動いてる)かで `size_t` (`__SIZE_TYPE__`)
> のサイズは変わります。

前指摘とも関係しますが、CPUのサイズに意味はありません。質問のIntelプロセッサーなら16bitモードも存在します。ハードウェア（CPU）にそのような機能があるとしても、プラットフォーム（OS）としてハードウェアのモードを設定し、併せて`size_t`が何ビットなのかを規定しています。その意味でLinuxのman
pageを読むのも適切ではありません。

同様にコンパイラーもあくまでプラットフォームの規定に従っているだけであり、コンパイラーがビット数を定めているわけではありません。余談ですが、Windowsプラットフォームにおいて`long
double`は64bitと定められていますが、[MinGWのgccはこの規定に従わず`long
double`を96bitとして扱っているため`printf`が正しく動作しない問題](https://ja.stackoverflow.com/a/34023/4236)が発生していたりします。その意味でコンパイラーが何であるかを意識するのも適切ではありません。

もちろん、質問のmacOSではなくLinuxについての情報を参考として載せるのは構いませんが、全体としてmacOSの情報であるかのようにミスリーディングしている印象を受けます。
CC BY-SA 4.0
2022-02-11T14:51:28.077
86275
2022-02-12T21:30:27.723
2022-02-12T21:30:27.723
4236
4236
86274
answer
macOS での C-Compilerは ~~gcc LLVM-gcc~~ 「Xcodeの gccを呼び出すとclangが呼び出される」のようです。  
Linuxの man pageだけど, GCCフロントエンドとして簡単に探したもの (他意はありません)

  * [size_t strlen(const char *s)](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/strlen.3.html) … `strlen()` は `size_t` を返す
  * [void *malloc(size_t size )](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/malloc.3.html)

`size_t` の大きさは環境依存で, ユーザー空間での gccでの場合は (カーネル空間の場合は話は別)

```

    typedef __SIZE_TYPE__ size_t;
    
```

参考 (stackoverflow.com):

  * (Linuxだけど) [Where is c++ size_t defined in linux](https://stackoverflow.com/questions/12490622/where-is-c-size-t-defined-in-linux)
  * (ちょっと昔だけど) [Why is there ambiguity between uint32_t and uint64_t when using size_t on Mac OS X?](https://stackoverflow.com/questions/11603818/why-is-there-ambiguity-between-uint32-t-and-uint64-t-when-using-size-t-on-mac-os)

* * *

OSというか CPUが 32bitモードか 64bitモード(で動いてる)かで `size_t` (`__SIZE_TYPE__`) のサイズは変わります。  
x86系 CPUの最近のものや, あるいは ARM系のパワフル方面のは, 32bitモードでも 64bitモードでもどちらでも稼働可能,
なはず。64bitモードで稼働していれば `__SIZE_TYPE__` は 8 Bytesです (`unsigned long int`)

* * *

#### (追記)

(macOS としてだけではなくモダンな) C言語での記述方法として, 以下の記事があります

  * [2016年、C言語はどう書くべきか (前編)](https://postd.cc/how-to-c-in-2016-1/)
  * [2016年、C言語はどう書くべきか (後編)](https://postd.cc/how-to-c-in-2016-2/)

内容そのままでは長いけれど, 下手に抜粋すると正しく伝わらないかもなので「システム依存の型」「その他の型」ついて引用します

> システム依存の型
>
> まだ「32 bitのプラットフォームでは32 bitのlong型、64 bitのプラットフォームでは64
> bitのlong型がいい」という不満があるようですね。
>
> プラットフォームに依存する2つの異なるサイズを使うため、 故意に コードを難しくすることを考えたくなければ、システム依存の型のために `long`
> を使おうとは思わないでしょう。
>
> この状況では、プラットフォームのためにポインタ値を保持する整数型、`intptr_t` を使うべきです。
>
> モダン32-bitプラットフォームでは、`intptr_t` は `int32_t` です。
>
> モダン64-bitプラットフォームでは、`intptr_t` は `int64_t` です。
>
> `intptr_t` は `uintptr_t` の形でもあります。
>
> ポインタのオフセットを保持するためには、名が体を表す通りの `ptrdiff_t` を使います。これは、ポインタの差分の値を格納するのに適した型です。

> その他の型
>
> 最も頻繁に使われるシステム依存の型は、`size_t`
> で、[stddef.h](http://pubs.opengroup.org/onlinepubs/7908799/xsh/stddef.h.html)
> に定義されています。
>
> `size_t` は基本的に「最大の配列インデックスを保持できる整数」ですが、プログラム内の最大のメモリオフセットを保持が可能という意味もあります。
>
> 実際の用途としては、`size_t` は `sizeof` オペレータの戻り値の型です。
>
> いずれにしても、`size_t` は全てのモダンプラットフォーム上の `uintptr_t` と同様になるよう、 実用的に
> 定義されます。そのため、32-bitプラットフォームでは、`size_t` は `uint32_t`
> であり、64-bitプラットフォームでは、`size_t` は `uint64_t` です。
>
> 他にも、`ssize_t` があり、これはライブラリ関数からの返り値として使われる符号付きの `size_t` で、エラーの際に `-1`
> を返します（注： `ssize_t` はPOSIXであり、Windowsインターフェースには適用されません）。
>
> では、あなたの関数パラメータ内で、任意のシステム依存型のサイズのための `size_t` を使うべきでしょうか。技術的には、`size_t` は
> `sizeof` の戻り値の型なので、バイト数を表したサイズ値を受け取る関数は全て `size_t` に変換できます。
>
> 他の用法には次のものが含まれます。： `size_t` はmallocに渡される引数の型であり、`ssize_t` は `read()` と
> `write()` の戻り値の型です（`ssize_t` が存在せず、戻り値がただの `int` であるWindowsの場合を除きます）。
CC BY-SA 4.0
2022-02-11T15:35:54.460
86276
2022-02-13T05:57:58.333
2022-02-13T05:57:58.333
3060
43025
86274
answer