char 是 C 语言中用于表示字符数据的基础类型,而 char * 则是字符指针,常用于字符串处理、字符数组访问以及动态内存操作。实际编程中,char 变量声明、字符指针声明以及指针赋值经常同时出现,但三者的含义并不相同。理解它们之间的关系,是掌握 C 语言指针和字符串操作的重要基础。
一、char变量的基本声明
最简单的 char 变量声明方式如下:
Cchar ch;
这里声明了一个名为 ch 的字符变量,可以保存一个字符。
赋值时通常使用单引号:
Cchar ch = 'A';
也可以在声明之后再进行赋值:
Cchar ch; ch = 'B';
需要注意,字符常量使用单引号,例如:
C'A' 'B' '0' ' '
而字符串使用双引号:
C"ABC" "Hello"
二者在 C 语言中的类型和存储方式不同。
例如:
Cchar ch = 'A'; char str[] = "A";
ch 保存的是单个字符,而 str 实际上是一个字符数组,内容包含字符 'A' 以及字符串结束符 ' '。
二、char指针变量如何声明
字符指针的声明形式非常简单:
Cchar *p;
这里的 p 是一个指针变量,其类型为 char *,表示它可以保存一个 char 对象的地址。
例如:
Cchar ch = 'A'; char *p = &ch;
此时内存关系可以理解为:
p │ ▼ ch 'A'
p 保存的是 ch 的地址,而不是字符 'A' 本身。
通过解引用运算符 * 可以访问 p 指向的字符:
Cprintf("%c ", *p);
输出:
A
还可以通过指针修改变量:
C*p = 'B'; printf("%c ", ch);
此时输出:
B
原因是 p 指向的就是 ch。
三、char指针赋值的常见方式
字符指针赋值主要取决于指针指向的数据来源。常见情况包括指向普通 char 变量、指向字符数组以及指向字符串字面量。
1. 指向char变量
这是最基础的形式:
Cchar ch = 'A'; char *p; p = &ch;
其中:
C&ch
表示获取 ch 的地址。
所以:
Cp = &ch;
就是把 ch 的地址赋值给字符指针 p。
完整示例:
C#includeint main(void) { char ch = 'A'; char *p = &ch; printf("字符:%c ", ch); printf("指针指向:%c ", *p); return 0; }
2. 指向字符数组
字符指针还可以指向字符数组的第一个元素:
Cchar str[] = "Hello"; char *p = str;
这里不需要写:
Cchar *p = &str;
因为数组名 str 在大多数表达式中会自动转换为指向第一个元素的指针,即:
Cstr
可以理解为:
C&str[0]
因此:
Cchar *p = str;
实际上让 p 指向:
H e l l o ↑ p
通过指针可以访问字符串:
Cprintf("%s ", p);
也可以访问单个字符:
Cprintf("%c ", *p); printf("%c ", *(p + 1));
输出分别为:
H e
3. 指向字符串字面量
还可以这样声明:
Cchar *p = "Hello";
这段代码在语法层面通常可以通过编译,但需要特别注意:不能通过 p 修改字符串字面量。
例如:
Cchar *p = "Hello"; p[0] = 'h';
这种写法会产生未定义行为。
更安全、更明确的写法是:
Cconst char *p = "Hello";
此时 p 表示指向不可通过该指针修改的字符数据:
Cconst char *p = "Hello"; printf("%s ", p);
如果确实需要修改字符串内容,应使用字符数组:
Cchar str[] = "Hello"; str[0] = 'h'; printf("%s ", str);
输出:
hello
四、char *与char的区别
很多初学者容易混淆:
Cchar ch; char *p;
这两个变量虽然都和字符有关,但用途完全不同。
char ch 保存字符数据:
Cchar ch = 'A';
而 char *p 保存地址:
Cchar *p = &ch;
可以通过下面的代码直观理解:
Cchar ch = 'A'; char *p = &ch;
此时:
Cch
表示字符本身。
C&ch
表示字符变量的地址。
Cp
保存这个地址。
C*p
表示访问这个地址对应的字符。
因此四者的含义分别是:
| 表达式 | 含义 |
|---|---|
ch | 字符变量的值 |
&ch | 字符变量的地址 |
p | 指针保存的地址 |
*p | 指针指向的字符 |
五、char指针赋值时的类型匹配
指针赋值必须关注类型。
例如:
Cchar ch = 'A'; char *p = &ch;
类型完全匹配。
而下面这种写法则存在类型问题:
Cint num = 100; char *p = #
&num 的类型是:
Cint *
而 p 的类型是:
Cchar *
两者并不匹配。
虽然某些情况下编译器可能只给出警告,但不应该忽略这种问题。通过错误类型的指针访问对象可能导致未定义行为。
如果确实需要处理不同类型的数据,应根据实际数据类型选择对应的指针类型。
六、一次声明多个char指针时的注意事项
C 语言声明语法中,* 属于变量声明符,而不是基础类型的一部分。这一点在一次声明多个变量时非常重要。
例如:
Cchar *p1, *p2;
这里 p1 和 p2 都是字符指针。
但下面的代码:
Cchar *p1, p2;
只有 p1 是指针,p2 是普通 char 变量。
也就是说:
char *p1
表示 p1 是 char *。
而:
char p2
表示 p2 是 char。
因此,如果需要声明多个字符指针,应该明确写成:
Cchar *p1, *p2, *p3;
为了提高代码可读性,实际项目中也可以分别声明:
Cchar *p1; char *p2; char *p3;
这样不容易产生误解。
七、char指针与字符数组的关系
字符指针和字符数组经常一起使用,但二者不能简单地认为是同一种变量。
例如:
Cchar str[] = "Hello"; char *p = str;
str 是字符数组,数组本身拥有实际存储空间。
而 p 是指针变量,它保存的是某个字符对象的地址。
可以执行:
Cp[0] = 'h';
因为 p 指向的是可以修改的字符数组:
h e l l o
但是下面这种情况不同:
Cchar *p = "Hello";
这里 p 指向字符串字面量,不应该通过 p 修改其内容。
因此,在需要修改字符串时,可以优先使用:
Cchar str[] = "Hello"; char *p = str;
如果只是读取固定字符串,可以使用:
Cconst char *p = "Hello";
八、使用char指针遍历字符串
由于字符串本质上是以 ' ' 结尾的字符序列,因此字符指针非常适合遍历字符串。
例如:
C#includeint main(void) { char str[] = "Hello"; char *p = str; while (*p != ' ') { printf("%c ", *p); p++; } return 0; }
程序会依次读取:
H e l l o
其中:
C*p
表示当前字符。
Cp++
表示让指针移动到下一个 char 对象。
由于 p 是 char * 类型,指针每增加 1,就会移动一个 char 对象的距离。
九、char指针与数组下标的对应关系
下面两种访问方式通常具有相同的效果:
Cstr[2]
和:
C*(str + 2)
例如:
Cchar str[] = "Hello"; printf("%c ", str[1]); printf("%c ", *(str + 1));
两次都会访问字符串中的第二个字符:
e
当使用字符指针时,也可以写:
Cchar *p = str; printf("%c ", p[1]); printf("%c ", *(p + 1));
这说明数组下标和指针运算在 C 语言中存在紧密关系。
十、char指针赋值中的NULL问题
定义指针变量时,如果暂时没有有效的指向对象,可以初始化为 NULL:
Cchar *p = NULL;
此时表示:
p 当前不指向有效的char对象
可以进行判断:
Cif (p != NULL) { printf("%c ", *p); }
不能直接对空指针解引用:
Cchar *p = NULL; printf("%c ", *p);
这种操作会产生未定义行为,实际运行时可能导致程序崩溃。
因此,字符指针在使用前最好确保它已经指向有效对象。
十一、char指针作为函数参数
字符指针在函数参数中也非常常见。
例如:
C#includevoid printString(const char *str) { printf("%s ", str); } int main(void) { char text[] = "Hello C"; printString(text); return 0; }
函数:
Cvoid printString(const char *str)
表示函数接收一个指向字符数据的指针,并且函数内部不会通过 str 修改这些字符。
如果函数需要修改传入的字符数组,则可以使用:
Cvoid changeString(char *str) { str[0] = 'h'; }
调用:
Cchar text[] = "Hello"; changeString(text);
此时原数组内容会被修改。
十二、常见错误写法分析
错误一:把字符直接赋给字符指针
Cchar *p = 'A';
这里 'A' 是字符常量,而 p 需要的是地址。二者类型不匹配。
如果需要指向字符变量,应写:
Cchar ch = 'A'; char *p = &ch;
错误二:把字符串赋值给char变量
Cchar ch = "A";
"A" 是字符串字面量,不是单个字符。
正确写法:
Cchar ch = 'A';
如果需要保存字符串,则使用:
Cchar str[] = "A";
或者:
Cconst char *str = "A";
错误三:修改字符串字面量
Cchar *p = "Hello"; p[0] = 'h';
这种写法不安全,因为字符串字面量不应被修改。
推荐:
Cchar str[] = "Hello"; str[0] = 'h';
或者在只读场景下:
Cconst char *p = "Hello";
错误四:忘记初始化指针
例如:
Cchar *p; *p = 'A';
p 没有指向有效的 char 对象,就直接进行解引用,属于危险操作。
正确方式可以是:
Cchar ch; char *p = &ch; *p = 'A';
十三、char指针声明与赋值的实用总结
常见写法可以归纳为以下几种:
Cchar ch = 'A';
表示定义一个字符变量。
Cchar *p = &ch;
表示定义字符指针并让它指向 ch。
Cchar str[] = "Hello"; char *p = str;
表示让字符指针指向字符数组的首元素。
Cconst char *p = "Hello";
表示使用字符指针访问字符串字面量,并明确不通过该指针修改内容。
Cchar *p = NULL;
表示暂时没有有效的指向对象。
掌握 char 与 char * 的区别后,再结合 & 取地址和 * 解引用,就能够正确理解大多数字符指针赋值代码。尤其需要牢记:普通 char 保存的是字符数据,char * 保存的是地址;字符常量使用单引号,字符串使用双引号;需要修改字符串时,应优先使用字符数组,而不是直接修改字符串字面量。