ホームページ php教程 PHP开发 文字配列と文字ポインタ

文字配列と文字ポインタ

Dec 12, 2016 pm 05:16 PM
文字列配列

问题1:

字符数组名可以作为左值吗?当然不行

比如 

char str[20] = {'h','e','l','l','o',' ','w','o','r','l','d'};

str++;

不可以这么干,因为字符数组名是一个常量指针,也就是是一个const char*

#include <stdio.h>

int main()
{
    char str[20] = {&#39;h&#39;,&#39;e&#39;,&#39;l&#39;,&#39;l&#39;,&#39;o&#39;,&#39; &#39;,&#39;w&#39;,&#39;o&#39;,&#39;r&#39;,&#39;l&#39;,&#39;d&#39;};
    printf("sizeof(str): %d\n",sizeof(str));
    printf("str: %s\n",str);
    str = str + 1;  //error
    return 0;
}
ログイン後にコピー

运行结果如下:

文字配列と文字ポインタ

当数组名为左值时,它的类型是字符数组;当数组名为右值时,它的数据类型是字符指针。

#include <stdio.h>
#include <string.h>
int main(void)
{
    char buf[10];
    char *p= “afdal”;
    buf = p;  //将一个char* 赋给一个char[10],类型不一样,必然不成功
    printf(“the buf is:%s\n”,buf);
    return 0;
}
ログイン後にコピー

问题2:

字符数组如何进行初始化?

#include <stdio.h>

int main()
{
    char ptr[20] = "hello world";  //success
    char str[20] = {&#39;h&#39;,&#39;e&#39;,&#39;l&#39;,&#39;l&#39;,&#39;o&#39;,&#39; &#39;,&#39;w&#39;,&#39;o&#39;,&#39;r&#39;,&#39;l&#39;,&#39;d&#39;};  //success
    char ctr[20];
    ctr = "hello world";  // error: incompatible types when assigning to type ‘char[20]’ from type ‘char *’
    return 0;
}
ログイン後にコピー

在给字符数组初始化的时候,会自动在字符数组的结尾加上'\0'

#include <stdio.h>

int main()
{
    char str[20] = {&#39;h&#39;,&#39;e&#39;,&#39;l&#39;,&#39;l&#39;,&#39;o&#39;,&#39; &#39;,&#39;w&#39;,&#39;o&#39;,&#39;r&#39;,&#39;l&#39;,&#39;d&#39;};
    printf("sizeof(str): %d\n",sizeof(str));
    printf("str: %s\n",str);
    int i = 0;
    while(*(str + i) != &#39;\0&#39;)  //判断是否加上&#39;\0&#39;
    {
        printf("%c\n",*( str + i++));
    }
    return 0;
}
ログイン後にコピー

运行结果如下:

文字配列と文字ポインタ

问题3:

字符数组越界访问能编译通过吗?

字符数组越界访问编译可以通过,没有报错,这样会出现很多的问题

#include <stdio.h>

int main()
{
    char str[12] = {&#39;h&#39;,&#39;e&#39;,&#39;l&#39;,&#39;l&#39;,&#39;o&#39;,&#39; &#39;,&#39;w&#39;,&#39;o&#39;,&#39;r&#39;,&#39;l&#39;,&#39;d&#39;};
    printf("%c\n",str[100]);
    return 0;
}
ログイン後にコピー

文字配列と文字ポインタ

打印为空

问题4:

字符数组和字符指针又有什么区别呢?

首先在内存的中位置不同,字符数组保存的字符串存放在内存的栈中,而字符指针指向的字符串保存在内存的静态存储区中。

其次字符数组保存的字符串属于字符串变量,可以被修改,而字符指针指向的字符串是属于字符串常量,不能被修改。

#include <stdio.h>

int main()
{
    char str[12] = {&#39;h&#39;,&#39;e&#39;,&#39;l&#39;,&#39;l&#39;,&#39;o&#39;,&#39; &#39;,&#39;w&#39;,&#39;o&#39;,&#39;r&#39;,&#39;l&#39;,&#39;d&#39;};
    char* ptr = "hello world";
    str[0] = &#39;f&#39;;
    ptr[0] = &#39;f&#39;;  //将字符指针指向的字符串修改,将出现段错误,因为该内存地址只读,因为该字符串属于常量
    return 0;
}
ログイン後にコピー

运行结果:

文字配列と文字ポインタ

段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址、访问了系统保护的内存地址、访问了只读的内存地址等等情况。

#include <stdio.h>

int main()
{
    char* ptr = "hello world";
    ptr[11] = &#39;!&#39;;  //往常量字符串末尾添加字符,相当于连接两个字符串,出错
    ptr[12] = &#39;\0&#39;;
    return 0;
}
ログイン後にコピー

这样也会出现段错误。

问题5:

字符指针是不是和字符数组名都是指针常量呢?

不是,字符指针,可以改变它的指向,也就是可以为左值。可以将一个字符指针指向一个字符串常量后又指向另外一个字符串常量。字符指针在为初始化之前,他是一个未定义的值,将指向任何可能的地方,所以在使用字符指针时一定要注意初始化。

#include <stdio.h>

int main()
{
    char* ptr;
    printf("ptr: %c\n",*ptr);
    return 0;
}
ログイン後にコピー

运行结果:

编译可以通过,但是ptr指向的内存地址是任意的

当然也可以将一个字符指针指向一个字符数组。

#include <stdio.h>

int main()
{
    char str[12] = {&#39;h&#39;,&#39;e&#39;,&#39;l&#39;,&#39;l&#39;,&#39;o&#39;,&#39; &#39;,&#39;w&#39;,&#39;o&#39;,&#39;r&#39;,&#39;l&#39;,&#39;d&#39;};
    char* ptr = str;
    printf("ptr: %s\n",ptr);
    return 0;
}
ログイン後にコピー

运行结果:

文字配列と文字ポインタ

问题6:

如果一个字符指针指向一个堆中动态内存。那么我们如何初始化该内存中的值?

#include <stdio.h>
#include <stdlib.h>

int main()
{ 
    char* str = (char*)malloc(sizeof(char)*20);
    printf("str:%p\n",str);     //str在堆中内存首地址
    str = "hello world";
    printf("str:%p\n",str);    //str在静态存储区内存首地址
    char* ptr = "I love you";
    printf("ptr:%p\n",ptr);    //str在静态存储区内存首地址
    return 0;
}
ログイン後にコピー

运行结果如下:

文字配列と文字ポインタ

很明显前后的地址不一样,前一个指向堆中的内存的地址,而后一个指向了静态存储区中的内存的地址。我本以为我通过上述方式进行初始化str的时候,我可以将堆中内存进行初始化,我发现我错了,字符指针将被重新指向。但是我想如果我们将str声明为const呢?那将会出现什么样的结果呢?

#include <stdio.h>
#include <stdlib.h>

int main()
{ 
    const char* str = (char*)malloc(sizeof(char)*20);  //我以为const将修饰str,使得str不能再作为左值而出现,我错了,const修饰的是由str指向的字符串,
    printf("str:%p\n",str);                            //该字符串不能再被修改
    str = "hello world";
    printf("str:%p\n",str);  
    return 0;
}
ログイン後にコピー

运行结果:

文字配列と文字ポインタ

如果我们将上述的代码做如下修改,程序还能编译通过吗?

#include <stdio.h>
#include <stdlib.h>

int main()
{ 
    const char* str = (char*)malloc(sizeof(char)*20);
    printf("str:%p\n",str);
    str[0] = &#39;h&#39;;    //这种赋值才是对str原来所指向的堆中的内存的赋值,编译不过,因为str指向的堆中的内存是只读的。
    str = "hello world";     
    printf("str:%p\n",str);
    return 0;
}
ログイン後にコピー

运行结果如下:

文字配列と文字ポインタ

上述就引发了我思考为什么const修饰的是str指向的字符串,而不是str本身呢?

原来,char* const str才是真正的修饰str本身,使得str为只读的,不能再被修改,也就是重新指向其他内存地址。而const char* str 和 char const* str 的意义是一致的,都是修饰的是*str。

#include <stdio.h>
#include <stdlib.h>

int main()
{ 
    char* const str = (char*)malloc(sizeof(char)*20);
    printf("str:%p\n",str);
    str[0] = &#39;h&#39;; 
    str = "hello world";      //变量str被重新指向,error,编译不过
    printf("str:%p\n",str);
    return 0;
}
ログイン後にコピー

运行结果如下:

文字配列と文字ポインタ

那么我们又该如何初始化一个字符指针指向的内存呢?

其实我们可以很清楚的分析,如果str是一个字符指针,也就是一个变量,变量是可以被重新赋值的,而每一个字符串本身是有一个地址的,str = “hello world”,必然就是改变了str的值,使得str保存着"hello world"的内存首地址,而一旦你将str = "I love you",必然str将保存这"I love you"的内存首地址,这都是毋庸置疑的。

#include <stdio.h>

int main()
{
    printf("hello world:%p\n","hello world");   //将打印出"hello world"的内存地址
    printf("I love you:%p\n","I love you");    //将打印出"I love you"的内存地址
    return 0; 
}
ログイン後にコピー

运行如下:

文字配列と文字ポインタ

下面我得回到这样一个问题?

str 和 *str的区别,很明显,str是一个指针,而*str是str指向的内存的存放的值,既然我们想改变str指向内存中的值,既*str的值,那么为何不将*str作为左值,来初始化str所指向内存的值呢?而*str 不就是 str[0]吗?所以很明显了。上述问题:那么我们又该如何初始化一个字符指针指向的内存呢?当然这个仅限于字符指针指向的是由mlloc分配的堆中的内存以及字符数组指向的栈中的内存。而字符指针如果指向字符常量,不可修改字符指针指向的内存的值,因为字符常量是只读的。

#include <stdio.h>
#include <stdlib.h>

int main()
{ 
    char* const str = (char*)malloc(sizeof(char)*20);
    printf("str:%p\n",str);
    str[0] = &#39;A&#39;;     //数组下标型赋值
    *(str + 1) = &#39;l&#39;;  //指针++型赋值
    str[2] = &#39;e&#39;,
    *(str + 3) = &#39;x&#39;;
    printf("str:%s\n",str);  
    printf("str:%p\n",str);
    return 0;
}
ログイン後にコピー

运行如下:

文字配列と文字ポインタ

#include <stdio.h>
#include <stdlib.h>
int main()
{ 
    char str[20]; 
    printf("str:%p\n",str);
    str[0] = &#39;A&#39;; 
    *(str + 1) = &#39;l&#39;;
    str[2] = &#39;e&#39;,
    *(str + 3) = &#39;x&#39;;
    //str[4] = &#39;\0&#39;;  //当我们没有手动给字符串结尾附上&#39;\0&#39;
    printf("str:%s\n",str);
    printf("str:%p\n",str);
    return 0; 
}
ログイン後にコピー

运行如下:

文字配列と文字ポインタ

上述的运行结果说明上述程序并没有自动给字符串结尾附上'\0',对于字符数组除非这样赋值,str[20] = "hello world",不然你就得手动给字符串附上字符串结尾'\0'。

#include <stdio.h>
#include <stdlib.h>

int main()
{ 
    char str[20]; 
    printf("str:%p\n",str);
    str[0] = &#39;A&#39;; 
    *(str + 1) = &#39;l&#39;;
    str[2] = &#39;e&#39;,
    *(str + 3) = &#39;x&#39;;
    str[4] = &#39;\0&#39;;
    printf("str:%s\n",str);
    printf("str:%p\n",str);
    return 0; 
}   
~
ログイン後にコピー

运行结果:

文字配列と文字ポインタ

附上一段我关于memcpy()和memmove()的代码的实现。

#include <stdio.h>
#include <stdlib.h>

void mem_copy(char* const dest, const char* const src)
{
    for(int i = 0; *(src + i) != &#39;\0&#39;; i++)
        *(dest + i) = *(src + i);
}

void mem_move(char* const dest, const char* const src)
{
    int i = 0;
    while(*(src + i) != &#39;\0&#39;)
        i++;
    for(; i != -1; i--)
        *(dest + i) = *(src + i);
}

int main()
{
    char* src = "hello world";
    char* const dest1 = (char*)malloc(sizeof(char) * 20);
    char dest2[20];
    mem_copy(dest1,src);
    mem_move(dest2,src);
    printf("dest1:%s\n",dest1);
    printf("dest2:%s\n",dest2);
    return 0;
}
ログイン後にコピー

当我们需要将内存的中的某一地址段的内容拷贝到内存中另一地址段中,以上是将字符串常量区的内容拷贝到堆中和栈中,我们可以看到我分别使用了malloc和字符数组,当然我们得考虑可能在拷贝的过程中会有地址段重叠的问题,重叠该怎么拷贝,解决方案很简单,就是从尾向前拷贝,即可。

运行结果如下:

文字配列と文字ポインタ

以上是我对c语言中字符串操作的一些理解

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Oracleでsplit()関数を使用する方法 Oracleでsplit()関数を使用する方法 May 07, 2024 pm 01:06 PM

SPLIT() 関数は、指定された区切り文字によって文字列を配列に分割し、各要素が元の文字列の区切り文字で区切られた部分である文字列の配列を返します。使用法には、コンマで区切られた値のリストを配列に分割する、パスからファイル名を抽出する、電子メール アドレスをユーザー名とドメインに分割するなどが含まれます。

Javaで文字列をソートする方法 Javaで文字列をソートする方法 Apr 02, 2024 am 02:18 AM

Java で文字列を並べ替える方法: Arrays.sort() メソッドを使用して、文字列の配列を昇順に並べ替えます。 Collections.sort() メソッドを使用して、文字列のリストを昇順に並べ替えます。文字列のカスタム並べ替えには Comparator インターフェイスを使用します。

C言語で\0は何を意味しますか C言語で\0は何を意味しますか Apr 27, 2024 pm 10:54 PM

C 言語では、\0 は文字列の終了マークであり、ヌル文字またはターミネータと呼ばれます。文字列はバイト配列としてメモリに格納されるため、コンパイラは \0 を介して文字列の末尾を認識し、文字列が正しく処理されることを保証します。 \0 仕組み: コンパイラは \0 に遭遇すると文字の読み取りを停止し、それ以降の文字は無視されます。 \0 自体はストレージ領域を占有しません。利点としては、信頼性の高い文字列処理、効率の向上 (終端を見つけるために配列全体をスキャンする必要がない)、比較と操作の容易さが挙げられます。

Javaのargsは何を意味しますか Javaのargsは何を意味しますか Apr 25, 2024 pm 10:15 PM

args は Java のコマンド ライン引数を表し、プログラムの起動時にプログラムに渡される引数のリストを含む文字列の配列です。これは main メソッドでのみ使用でき、デフォルト値は空の配列で、各パラメーターはインデックスによってアクセスできます。 args は、プログラムの開始時に入力データを構成または提供するためにコマンド ライン引数を受け取って処理するために使用されます。

Javaのargsは何を意味しますか Javaのargsは何を意味しますか May 07, 2024 am 02:24 AM

args は Java の main メソッドの特別なパラメータ配列で、コマンド ライン パラメータまたは外部入力の文字列配列を取得するために使用されます。 args 配列にアクセスすることで、プログラムはこれらの引数を読み取り、必要に応じて処理できます。

PHP機能への人工知能技術の応用 PHP機能への人工知能技術の応用 May 01, 2024 pm 01:15 PM

AI テクノロジーと PHP の機能を組み合わせて、アプリケーションの機能を強化しました。具体的な AI アプリケーションには、Naive Bayes などの機械学習アルゴリズムを使用したテキストの分類が含まれます。単語のセグメンテーションやステミングなどの自然言語処理技術を使用して、詳細なテキスト分析を実行します。

C言語環境で漢字をソートするにはどうすればよいですか? C言語環境で漢字をソートするにはどうすればよいですか? Feb 18, 2024 pm 02:10 PM

C言語プログラミングソフトウェアに漢字ソート機能を実装するにはどうすればよいですか?現代社会において、漢字ソート機能は多くのソフトウェアに欠かせない機能の一つとなっています。ワープロ ソフトウェア、検索エンジン、データベース システムのいずれにおいても、中国語のテキスト データをより適切に表示および処理するには、中国語の文字を並べ替える必要があります。 C言語プログラミングで、漢字ソート機能を実装するにはどうすればよいですか?一つの方法を以下に簡単に紹介します。まず、C言語で漢字ソート機能を実装するには、文字列比較関数を使用する必要があります。ラン

C++ 関数はプログラムのパフォーマンスにどのような影響を与えますか? C++ 関数はプログラムのパフォーマンスにどのような影響を与えますか? Apr 12, 2024 am 09:39 AM

C++ プログラムのパフォーマンスに対する関数の影響には、関数呼び出しのオーバーヘッド、ローカル変数、およびオブジェクト割り当てのオーバーヘッドが含まれます。 関数呼び出しのオーバーヘッド: スタック フレーム割り当て、パラメーター転送、および制御転送が含まれます。これは、小規模な関数に大きな影響を与えます。ローカル変数とオブジェクト割り当てのオーバーヘッド: ローカル変数やオブジェクトの作成と破棄が大量に行われると、スタック オーバーフローやパフォーマンスの低下が発生する可能性があります。

See all articles