你知道char *s和char s[]的區別嗎?

2021-02-16 C語言與C++編程

在一個夜深人靜的晚上,有一個讀者給我發了一個C語言題目。他問我,發哥,幫我看看這個代碼有什麼問題。我看了代碼之後,心裡一陣恐慌。我自認為我不是C語言高手。但我確實是一個喜歡解決問題的男人。就是在這樣的背景驅使下,我寫下了這篇文章。

char *str1 = "hello";
char str2[] = "hello";

我們看這兩個定義。我們說這個是定義而不是聲明,是因為定義在內存裡面分配了房子。而聲明,只給了個房產證卻不給你分房子。

str1 是 char *類型 。它是一個指針,這個指針指向一個字符串。

str2 是 char [] 類型。它是一個數組,他代表了這堆內存空間。

「hello」字符串在內存中是這樣存放的

我之前寫過一個不同變量地址分配在內存不同區域的文章,有不清晰的可以再回去看看。

str1 str3都是指向字符串的指針,而且這個字符串是保存在字符串常量區的。這個常量區裡面的東西是不能被修改的。編譯器讓他們指向了同一個地址。這個地址保存的東西是 「hello」這個字符串。

大家看看下面這個代碼有什麼問題?

#include "stdio.h"
#include "stdlib.h"
#include "string.h"

int main(void)
{
char *str1 = "hello";
char *str3 = "hello";
char str2[] = "hello";

memcpy(str3,"worldtest",strlen("worldtest")+1);

printf("str1:%s str3:%s str2:%s\n",str1,str3,str2);

str3 = "world";
printf("str1:%s str3:%s str2:%s\n",str1,str3,str2);
printf("hello,world\n");
return (0);
}

memcpy嘗試向一個非法的地址拷貝東西,這個是不允許的。為什麼說這個地址非法呢?因為字符常量區裡面的內容,只可以讀,不可以寫。

如果改成這樣的呢?應該輸出什麼結果呢?

#include "stdio.h"
#include "stdlib.h"
#include "string.h"

int main(void)
{
char *str1 = "hello";
char *str3 = "hello";
char str2[] = "hello";

//memcpy(str3,"worldtest",strlen("worldtest")+1);

printf("str1:%s str3:%s str2:%s\n",str1,str3,str2);

str3 = "world";
printf("str1:%s str3:%s str2:%s\n",str1,str3,str2);
printf("hello,world\n");
return (0);
}

我之前在文章裡面討論一個問題,我們說指針的時候,要說指針變量。指針變量保存的內容是一個地址。既然是變量,那麼保存的地址是可以變化的。只要類型符合。都可以保存。

同樣的,在上面的例子中,如果我們嘗試這樣

str1[1] = 'a';

這樣也是錯誤的。這樣也是寫操作了非法的地址。

試試下面這段代碼

#include <stdio.h>
int main(){
char* str1="Hello";
printf("\nstr1: %s, address: %p, sizeof(str1): %u", str1, str1, sizeof(str1));
str1 = "world";
printf("\nstr1: %s, address: %p, sizeof(str1): %u", str1, str1, sizeof(str1));
return 1;
}

輸出


str1: Hello, address: 0000000000404000, sizeof(str1): 8
str1: world, address: 0000000000404031, sizeof(str1): 8
--
Process exited after 0.0226 seconds with return value 1
請按任意鍵繼續. . .

通過賦值運算後,str1的值也發生了改變。

但是str2情況會不一樣,str2是一個數組。

既然是數組,我們看看這段小代碼

#include<stdio.h>
int main(){
char str2[] = "hello";
printf("\nstr2: %s, address: %p, sizeof(str2): %u", str2, str2, sizeof(str2));
str2[2] = 'A';
printf("\nstr2: %s, address: %p, sizeof(str2): %u", str2, str2, sizeof(str2));
strcpy(str2, "world");
printf("\nstr2: %s, address: %p, sizeof(str2): %u", str2, str2, sizeof(str2));
return 1;
}

輸出日誌

str2: hello, address: 000000000062FE10, sizeof(str2): 6
str2: heAlo, address: 000000000062FE10, sizeof(str2): 6
str2: world, address: 000000000062FE10, sizeof(str2): 6
--
Process exited after 0.04063 seconds with return value 1
請按任意鍵繼續. . .

送一個圖

晚上回來我寫了一個小程序。大家看看

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

const int a = 1;
const int a1 = 1;
char * s = "hello";

int main()
{
const int b = 2;
const int b1 = 2;
char * s1 = "hello";

printf("s:%p s1:%p\n",s,s1);
printf("a:%p a1:%p b:%p b1:%p\n",&a,&a1,&b,&b1);

return 1;
}

輸出如下:

s:0000000000404008 s1:0000000000404008
a:0000000000404000 a1:0000000000404004 b:000000000062FE14 b1:000000000062FE10

--
Process exited after 0.03901 seconds with return value 1
請按任意鍵繼續. . .

可以看到,s,s1,a,a1在一個內存區域。這個內存區域的內容是不允許改變的。如果你對這裡的內存區域賦值,就會出現段錯誤。

但是b和b1這個內存區域大家看看。我們可以寫個小代碼測試一下。

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

const int b = 2;

int main()
{
const int b1 = 2;
int *p = &b1;
printf("b1:%d\n",b1);
*p = 3;
printf("b1:%d\n",b1);

return 1;
}

輸出:

b1:2
b1:3

--
Process exited after 0.0403 seconds with return value 1
請按任意鍵繼續. . .

但是我們寫成這樣呢?

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

const int b = 2;

int main()
{
const int b1 = 2;
int *p = &b;
printf("b:%d\n",b);
*p = 3;
printf("b:%d\n",b);

return 1;
}

輸出:

b:2

--
Process exited after 3.743 seconds with return value 3221225477
請按任意鍵繼續. . .

如果放到gcc下,可以看到,執行到代碼

*p = 3;

會出現段錯誤。因為訪問了不能訪問的地址。這也就是我們很多時候給空指針賦值出現段錯誤的原因。操作了非法的地址。

好了,就瞎BB這麼多,如果覺得有用,可以留言一起討論下。

相關焦點

  • 「C/C++中char* 和 char「」區別
    char b[]=」hello2」; printf(「a=%s, b=%s」, a, b); 其中a是一個指向char變量的指針,b則是一個char數組(字符數組),其次 ,很多時候二者可以混用,像函數傳參數的時候,實參可以是char*,形參可以是 char[],比如:void fun1(char b[]){ printf
  • C語言中char int轉換問題
    語言 單引號和雙引號的區別1、含義不同。因此s的含義其實和十進位數115的含義是一致的。而用雙引號引起的字符串,代表的是一個指向無名數組起始字符的指針。2、大小不同。用單引號引起的一個字符大小就是一個字節。而用雙引號引起的字符串大小是字符的總大小+1,因為用雙引號引起的字符串會在字符串末尾添加一個二進位為0的字符 。
  • Oracle to_char的用法的描述
    以下的文章主要是對Oracle to_char 的用法的描述,相對而言Oracle to_char能在很短的時間裡被廣泛的應用,說明它的可實際應用性還是佔優勢的,以下就是文章的具體內容的介紹,希望你會有所收穫。
  • MySQL - char 和 varchar
    通常理解 char 是定長字符類型,varchar 是變長字符類型。char 字符類型會用空格填充空餘的空間,varchar 保存的是實際長度的數據。這帶來的影響是,查詢 varchar 字符類型的數據要先提供變成欄位列表中的記錄得到這個數據的長度,而 char 字符類型的數據是定長,不需要單獨獲取,char 往往有著比 varchar 更好的讀寫效率,但是浪費一定存儲空間。
  • 在C++中將string轉換為char數組
    convert std::string to char[] or char* data type』。輸入 : string s = "linuxmi" ;輸出 : char s[] = { 'l', 'i', 'n', 'u', 'x', 'm', 'i' } ;輸入 : string s = "python" ;輸出 : char s[] = { 'p', 'y', 't', 'h', 'o', 'n' } ;一種方法是將string
  • C語言char字符串與中文編碼的坑
    01020304050607080910111213141516void subEstimate(const char* str, const char* sub) {    const char* position = strstr(str, sub);    if (position == NULL) {        printf("\"%s\" is Not substring of \"%s\"!
  • CHAR與VARCHAR面面觀
    本篇文章主要會介紹字符串類型char及varchar的用法及區別。本文實驗環境為MySQL 5.7.23版本,存儲引擎為Innodb,sql_mode採用嚴格模式,字符集是utf8。▍1.CHAR類型介紹我們平時使用char類型定義欄位時,往往會指定其長度M,即char(M)。
  • Oracle學習: to_char轉數字小結
    ①輸出的是阿拉伯數字,不是對應的「星期—」格式,所以你要寫個對照表  ②如果你是個苦逼的程式設計師,你會忘記,java 裡「星期日」是 一周的開始,你的顯示就可能有問題  而如果是SQL,則一句話:  selectto_char(sysdate,'
  • MySQL的char與varchar詳解
    CHAR和VARCHAR都可以用來表示字符類型,其不同在於兩者的存儲與檢索、最大長度和尾部空格串的處理等方面。1 基礎總結下表是CHAR和CARCHAR的知識總結:char與varchar的基礎總結以下幾點需要特別引起注意:1> 雖然在聲明時,兩種類型的m都可以指定為0,實際上最長長度為0隻能存放空字符串。
  • C語言中的char類型也有signed和unsigned?字符也有正負之分嗎?
    C語言中的 unsigned int 和 signed int 類型的區別,相信即使是初學者也是清楚的,無非就是最高位是否用來做符號位而已。但是最近有讀者問我,為什麼 char 類型也要區分 unsigned char 和 signed char 型呢?
  • 【答疑釋惑】C語言中 scanf_s和 scanf 區別是什麼?
    ;函數 scanf() 是從標準輸入流stdio (標準輸入設備,一般是鍵盤)中讀內容的通用子程序,可以說明的格式讀入多個字符,並保存在對應地址的變量中。 其調用形式為: scanf("<格式說明字符串>",<變量地址>);變量地址要求有效,並且與格式說明的次序一致。
  • 【C++】搞懂char與wchar_t字符串
    來分析一下代碼,其中str1、str3、str4是一個東西(str3區別只是內存在堆上),str2是字面值常量,str5是單純的字符數組。1.1. 常規字符串對於str1、str3、str4這種正常的字符串,就可以隨意拿字符串函數和下標訪問,進行各種操作。
  • 理解(*(volatile unsigned char *)0x5F
    理解#define SREG (*(volatile unsigned char *)0x5F)這樣的定義,總是感覺很奇怪,不知道為什麼,今天終於有了一點點心得,請大蝦們多多批磚~~~本文引用地址:http://www.eepw.com.cn/article/201611/318876.htm 嵌入式系統編程,要求程式設計師能夠利用C語言訪問固定的內存地址。
  • 乾貨:你知道數組和指針有什麼區別嗎?
    另外,公開寫作則會給你的寫作增加很多維度的外部壓力,你會想如何讓別人更好地理解我要表達的意思;如何傳遞更多價值,讓別人讀完有所收穫;如何讓更多人看到;如何讓別人讀得下去;如何排版讓大家看得更舒服;二、數組和指針有什麼區別?
  • MFC下的cstring與char互相轉換方法
    Mfc下的cstring與char互相轉換方法本次課程主要來為大家講一下平時我們總是在mfc下環境開發中使用的char類型的數組和cstring格式的轉換,還有在qt下該如何轉換,送給有需要的小夥伴們。
  • 電腦程式的思維邏輯 (8) - char的真正含義
    看似簡單的char通過前兩節,我們應該對字符和文本的編碼和亂碼有了一個清晰的認識,但前兩節都是與程式語言無關的,我們還是不知道怎麼在程序中處理字符和文本。賦值時把常量字符用單引號括起來,例如:char c = 'A';char z = '中';但我們在第3節拋出了一個問題,為什麼字符類型也可以進行算術運算和比較?char的本質到底是什麼呢?
  • 利用CHAR函數,產生字符「世界盃」
    CHAR函數是我們在學習工作中不是經常會碰到的函數,出現的頻率不是很高,但能夠了解掌握這個函數,對我們的工作還是有很大幫助的,這個函數可以稱之為函數大神,是因為它不為大多數人熟悉,但功能簡單強大,有著不可思議的廣度和深度。
  • char* 轉 LPCWSTR 解決方案
    (點擊上方公眾號,可快速關注)來源: 周旭光,2013-04-05http://blog.csdn.net/zhouxuguang236/article/details/8761497如有好的文章投稿,請點擊 → 這裡查看詳情;在Windows編程中,經常會碰到字符串之間的轉換,char
  • Char RNN原理介紹以及文本生成實踐
    在圖一的例子中,根據前兩個字符產生的狀態和第三個輸入「l」預測出的下一個字符的向量為<0.1, 0.5, 1.9, -1.1>,最大的一維是第三維,對應的字符則為「0010」,正好是「l」。這就是一個正確的預測。但從第一個「h」得到的輸出向量是第四維最大,對應的並不是「e」,這樣就產生代價。學習的過程就是不斷降低這個代價。
  • CHAR函數中居然蘊含著這麼多秘密
    - 1 - char函數語法:char(數字)