1. 程式人生 > 其它 >C語言中 char s[] 和 char* s 的區別

C語言中 char s[] 和 char* s 的區別

char s[] 和 char* s 的區別


C語言指標可以代替陣列使用

1、陣列本質

陣列是多個元素的集合,在記憶體中分佈在地址連續的單元中,因此可以通過其下標訪問陣列的不同陣列。
例如:
下面展示一些

char s[3] = "0x1";
printf("s2字串:\n");
printf("%c\n", s[0]);
printf("%c\n", s[1]);
printf("%c\n", s[2]);

2、指標

指標也是一種變數,只不過它的記憶體單元中儲存的是一種標識其他位置的地址,而地址也是整數,在32位平臺下,就是32位,4個位元組(bytes)。

指標的指向

指標的指向是指 指標變數所儲存的其他的地址單元中 所存放的資料型別。
例如:

int *ptr;//ptr指標儲存的地址所在記憶體單元中的資料型別是整型
float *p;//這個p指標指向的記憶體地址存放的元素型別就是浮點型

而不管指向的資料型別是哪種,其實對於指標本身的值永遠是整型,因為它儲存的地址就是整數

3、字元陣列

字元陣列首先是陣列,陣列中的元素都是字元。

char s[10];//定義了一個含有10個元素的陣列,元素型別為字元 %c

C語言中定義一個變數時,可以初始化,如下:

char s[10] = {"hello world"};

當編譯器遇到上面的定義和賦值時,會將 hello world 和 \0 依次逐個填入連續陣列記憶體中。

C語言中是沒有真正的字串型別,可以通過字元陣列表示字串,並且字元陣列的元素地址也是連續的。C語言中規定陣列代表陣列所在記憶體位置的首地址,即 str字串是等於str[0]的,str = &str[0];

對於printf("%s",str); 為什麼用首地址就可以輸出字串?

因為對於C語言中字串常量的本質就是一個地址,例如:

char  *s ;
s = "Hello";

這裡把一個字串賦值給一個字串指標變數,起始就是C語言中編譯器會給字串常量分配地址,如果"Hello", 儲存在記憶體中的 0x3000 0x3001 0x3002 0x3003 0x3004 0x3005 ,可以看出真正的意義就是
s = “Hello” = 0x3000
因此我們可以把Hello看做是字串,而編譯器會把他看做是地址 0x3000 ,即字串常量的本質就是代表它的第一個字元的地址。

char *s;
s = "Hello";
printf("%p\n",s);//%p代表表示按十六進位制輸出資料,如果輸出資料不夠8位數,則左邊補0


輸出00796BD0,也就是"Hello"的首地址。

對於字元陣列:

char str[10] = “Hello”;
也就是說str = &str[0]
C語言中操作字串是通過它在記憶體中的儲存單元的首地址進行的,這是字串的終極本質。。。

4、char * 與 char a[ ]

對於char *s 與 char a[ ] :
a代表的是字串的首地址,而s代表的這個指標儲存字串的首地址(第一個字元的地址),即可以看做: s =a ,即可以將陣列名賦值給指標表示地址,但是 不能這樣賦值 a = s ,即:不能將指標賦值給陣列名
因為陣列名是一個常量,是不可以修改的
可以通過如下方式訪問陣列元素:

char a[] = "Hello";
char*s = a;
int i;
for(i = 0;i < strlen(a);i++)
{
printf("%c",s[i]);
//printf("%c",*s++);也可以
}

字元指標可以用 間接操作符 * 取其內容,也可以用陣列的下標形式 [ ],陣列名也可以用 *操作,因為它本身表示一個地址 。。

比如 printf("%c",*a); 將會打印出 ‘H’,

char * s 與 char a[ ] 的本質區別

當定義char a[10]的時候,編譯器會給陣列分配10個單元,每個單元的資料型別都是字元,而定義char *s時,s是一個指標變數,只佔4個位元組,32位,用來儲存一個地址。
sizeof(a) = 10,sizeof(s) = 4
a的長度是10,s的長度是4,因為s是一個int型

printf("%p",s)----------這個表示 s 的單元中所儲存的地址。

printf("%p",&s);--------這個表示變數本身所在記憶體單元地址。。。。,不要搞混了。。

總結:char * s 只是一個儲存字串首地址的指標變數,char a[]是許多連續的記憶體單元,單元中的元素是char型,char * 和 char a[]具有相同的效果,源於字串的本質,即給一個字串地址,便可以操作字串,但char* 和 char a[]的本質屬性不一樣。

5、char ** 和char *a[]

char *a[]

[]的優先順序高於 * ,所以先是a[],它是一個數組,陣列中的元素是char型別,char元素是一個變數,儲存地址。
對於char *a[ ] = {“Dog”,“Cat”,“Chicken”};
陣列中元素是字串,sizeof(a)是12,並不是4+4+8,是因為字串常量的本質是地址,a陣列中元素是char *指標,指標變數是整數型別,佔4個位元組,則3個元素就是12個位元組。

char *a[] = { "Dog","Cat","Chicken" };
printf("%p %p %p \n", a[0], a[1], a[2]);

可以看到陣列中的三個元素儲存了三個記憶體地址,這三個地址代表了三個字串的首地址,而不是字串本身,且三個地址不是連續的,它是編譯器為"Dog",“Cat”,“Chicken” 分配的記憶體空間的地址,因此沒有關聯。

char *a[] = { "Dog","Cat","Chicken" };
printf("陣列元素單元本身的地址:%p %p %p \n", &a[0], &a[1], &a[2]);//陣列元素單元本身的地址


可以看到三個元素單元所在的地址是連續的,每個地址相差四個位元組。

char ** s

char ** 為二級指標,s儲存一級指標char *的地址,
例如:

char *a [ ] = { "Dog","Cat","Chicken" };
char **s =   a;//---有問題

陣列a代表陣列元素記憶體單元的首地址,,即a = &a[0] = 010FFD44,即 *s = 001D6BE0 = “Dog”,
這樣可以通過s訪問a中的資料

printf(“%s”,*s);
printf("%s",a[0]);
printf("%s",*a);

三個一樣,,但是不能把a = s,因為a是一個常量;

char **s = “hello world”; ------是錯誤的;

因為s的型別是char ** ,而 “hello world”的型別是char *;

雖然都是地址, 但是指向的型別不一樣,因此,不能這樣用。,從其本質來分析,“Hello world”,代表一個地址,比如0x000001,這個地址中的內容是 ‘H’,為char型,而s也儲存一個地址,這個地址內容是char*,是一個指標型別。

char  **s;
*s = "hello world";

編譯沒有問題,但是 printf("%s",s),就會崩潰, printf ("%s", s); 時,首先得有s 儲存的地址,再在這個地址中找到 char * 的地址,即*s;

若s = 0x1000;
在0x1000所在的記憶體單元儲存了“hello world”的地址0x000001,*s = 0x000001,這樣printf("%s",*s)會先找到0x1000,然後找到0x000001,如果直接使用char **s,令 * s = “hello world”,s變數中儲存的是一個無效隨機不可用的值,不知道他指向哪裡,所以用char **s時,要給他分配一個記憶體地址。

char  **s ;
s = (char **) malloc(sizeof(char**));
*s =  "hello world";

這樣s給分配了一個可用的地址,s = 0x1234,然後再地址0x1234所在的記憶體中的位置,儲存了“hello world”的值。

下列程式中,定義了字元指標s,s中存放了字串"message"的地址。

#include  <stdio.h>
void  buf( char **s)
{
	*s = "message";
}
int main()
{
	char *s ;
	buf(&s);
	printf("%s\n",s);
}

即:二級指標儲存的是一級指標的地址,它的型別就是指標變數,一級指標儲存的是指向資料所在的記憶體單元的地址。

參考:
https://blog.csdn.net/daiyutage/article/details/8604720