首先明确以下几点:
1.字符串是一个数组,C语言中没有字符串类型的,字符串是由字符元素组成的数组。
2.%s用于输出字符串类型的内容,%c输出单个字符。
3.一般讲到常量指针const char*,基本都是针对字符串的。比如const char* p=“abc"。(“abc”实际上是一个地址,所以应该赋值给一个指针变量。)当然,对字符常量,const chat* p=‘a’也可以,但是改语句的作用是把字符常量a的ASCII码赋值给指针变量p,这在实际应用中没有什么意义。
4.因为前几天一直纠缠于字符常量和字符串常量中,所以错误的理解成了const char* p='a',也是将字符常量a的地址给到指针变量p了。但要注意,单个的字符常量不是数组,只有涉及到数组时,才有指针变量时字符串常量首字符的存储地址这一说法,单个字符串不是数组,根本就没有什么‘a’表示字符常量a的存储地址这种说法!这和第二条也是相对应的。
就记住:const char*基本是和字符串,注意是字符串,是一个数组相对应的!
5.const char*是针对字符串来说的,const char* 变量名[ ],是指针数组,是一个“二级指针”,里面存储的是指针变量的地址,所以是二级指针。
6.字符串是一个字符数组,即使是"A",也是一个字符串,也应该用char s[ ]="A",而不是char s="A"。
7.只有对于数组,才有数组名为第一个元素所在地址,而且数组名是一个指针常量。也就是对于字符串,才有数组名是一个指针,对于单个的字符,不是数组,没有说字符变量名是一个地址的说法。
8.看一下,犯过错误。c语言中不能将字符串赋值给字符数组https://blog.csdn.net/tjh1998/article/details/114628498?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-6.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-6.no_search_link
下面看程序实例,关于更好的理解printf 输出字符、字符串,以及const char* 和字符串数组:
#include
#include
#define MAX 3
int main(int argc, char* agrv[])
{
const char* a = 'a';
//a是一个字符常量,不是数组,这个语句的意思是把指针a赋值为字符'a'的ASCII码。
//const char* q = { "abc", "123" };
//这样写是不对的,这是字符串数组,应该是char* q[],这实际上是一个数组的数组,因为字符串本身就是数组。注意,数组元素就是要写成[]。
char* e = 'd';//此语句的含义也是把字符d的ASCII作为地址赋值给指针变量n
//这样表达是不对的,因为'd'是单个字符,不是一个数组,没有说首个元素的地址什么的,这样写本身就是错误的。(本人比较喜欢重复说,哈哈)
//字符串是一个数组
printf("************************************\n");
printf("%d\n", e);//输出的是字符d的ASCII码100,因为e的值是'd'
// printf("%c\n", *e);//这个语句是不能执行的,这个语句的意思是给一个指针变量复制了字符
//常量'd'的ASCII地址,地址为字符d的ASCII码100的内存是不一定能被访问的。
/*
同样也就解释了之前的疑问,为什么const char*是针对字符串,而不是字符来说的呢,
因为单个字符是一个数组,没有说'a',就表示a这个常量字符的说法。而字符串是数组,有地址传递的这个概念。 当然,字符常量也是存储在系统管理的常量存储区中的。*/
// //=====================下面的程序是对于%s和%c的使用================================
printf("***************************\n");
//
char* k = "abc";//(实际上就是const char* k="abc";)
printf("pointer k is %p\n", k);//输出字符串abc的首地址
// printf("%s\n", *k);
/*不能执行,因为k只是存储了字符串的首地址,*k实际上只是首字符a,单个字符不能用%s输出。*/
printf("%c\n", *k);//可以执行,输出首字母a
printf("%s\n", k);
/*输出字符串abc。这是产生了一个疑问,为什么k前面不用加取值符号*就可以输出字符串常量abc呢。如果是*k,指的其实是首字符a,如果再搭配%s,肯定是有错误的。而且printf输出%s形式的数据时,后面跟的量应该是字符串指针或者字符串数组名。对于字符数组,采用遍历循环的方式才能把每个字符依次输出。*/
printf("**********************\n");
//实际上,字符串数组名是一个二级指针,是指针的指针!
const char* n[] = {"abcdef", "BC" };
//%s后面要跟一个指向字符串常量的一级指针
printf("%s\n", n[0]);//认为输出a,结果是abcdef,因为n[0]是字符串常量abcdef的首地址。
printf("%s\n", n);//乱码,这个n实际上是一个地址的地址,是一个二级指针,不是n[0]
printf("%s\n", *n);//输出abcdef,相当于n[0],这也就是*数组名=数组第一个元素的值。
/*
n[0]就明确表明了是n[]数组的第一个元素,与数组名n是不一样的,n表示的是n[0]的地址。
*/
//下面验证一下:
printf("数组名n的值:%p\n", n);
printf("n[0]的地址 %p\n", &n[0]);
printf("n[0]的值 %p\n", n[0]);
printf("abcedf的地址:%p\n", "abcdef");
for (int i = 0; i < 2; i++)
{
printf("数组n %s\n", *(n + i));
/*
结果却是依次输出了abcdef和BC,字符串数组指针和字符串指针的使用是不同的,加了*后再输出才是字符串,不是单个字符
为什么呢,注意区分字符串和字符串数组,字符串数组的数组名实际上是一个二级指针,使用*解引用之后才是一个一级指针,才能搭配%s使用!
*/
}
for (int i = 0; i < 2; i++)
{
printf("数组n %s\n", (n + i));
//这样输出是乱码,相当于输出的是第二个元素所在的地址
}
printf("**********************\n");
printf("看看对于字符串能不能用指针一次性输出\n");
const char* g = "abcde";
printf("pointer g 表示的字符串:%s\n", g);//输出abcde
printf("pointer g 表示的字符:%c\n", *g);//打印字符串的首字符a
printf("%c\n", *"ABC");//A
printf("%c\n", "ABC");//???没有什么意思,输出一个不知道什么的值
//下面需要看一下字符串数组在内存中的存储方式
const char* name[] = { "ABC", "BCD", "DEC" };
for (int i = 0; i < MAX; i++)
{
printf("数组name:\n name[%d]=%c\n", i, *(name[i]));
//依次输出A、B、D
}
for (int i = 0; i < MAX; i++)
{
printf("数组name:\n name[%d]=%s\n", i, (name[i]));
//依次输出ABC、BCD、DEC
}
printf("**********************\n");
//printf("%s\n", *("AB"));//wrong,程序不运行,%s不能搭配字符使用
//printf("%s\n", "A");//输出A
printf("%c\n", *"AB");//A
printf("%c\n", *"A");//A
printf("%s\n", "ABC");//ABC
printf("**********************\n");
char d[5] = "hello";//字符串本身就是一个数组
printf("%s\n", d);//乱码 hello后面跟一堆垃圾值,因为在分配的空间中没有结束字符\0
// 注:%s后面跟的,可以是字符串指针,或者字符数组名。
/*
字符串就是字符组成的数组,但是后面多了一个结束符号\0;
别字符串指针写多了,再写成char a="abc",应该是char a[]="abc"...*/
return 0;
}
要注意字符串数组的数组名是一个“地址的地址”,要*之后才能搭配%s输出字符串,字符串指针const char* 直接搭配%s即可输出字符串。
以此类推,凡是指针数组,数组名都是相当于一个"二级指针",如果想用数组名输出数组元素时,使用解引用*后才得到一个一级指针。
如:
#include
#include
int main(int argc, char* agrv[])
{
const char* names[] = { "a", "b", "c" };
for (int i = 0; i < 3; i++)
{
printf("name[%d]=%s\n", i, names[i]);//这是直接用数组元素输出各个数组成员,数组元素本身就是一级指针。
}
//下面看一下如果用数组名输出各个数组元素,需要采用指针移位的方式,而且因为指针数组的数组名是相当于一个二级指针,要使用*进行解引用才可以。
const char* m[] = { "as", "bs", "cs" };
for (int i = 0; i < 3; i++)
{
printf("m[%d]=%s\n", i, *(m + i));
}
//输出首字母
//可以看出,如果想要输出单个字符,%c后面配合的应该直接是字符变量或者字符指针的解引用。
//这与%s输出字符串是不同的,根本原因还是在于字符串本身就是数组,字符却不是一个数组。
const char* s[] = { "as", "bs", "cs" };
for (int i = 0; i < 3; i++)
{
printf("s[%d]=%c\n", i, **(s + i));
}
return 0;
}
其实说了这么多,关键就在于%s后面跟的应该是一级指针,如果用指针数组名输出数组元素,要解引用,使用*指针数组名,才能搭配%输出正确的字符串。如果直接使用指针数组的成员,也就是直接使用指向字符串的指针,那么直接%s,指针数组名[i]就可以了。实际上,数组名也是一个指针常量呢,其实就是一个地址,相当于%s,后面接的就是指向字符串的指针。
下面再运行一下这些程序看看结果,加深对指针,数组,数组名,字符以及字符串的认识。
#include
#include
int main(int argc, char* agrv[])
{
int array[3] = { 1, 2, 3 };
int* arr = array;
int size = sizeof(array)/sizeof(int);
for (int i = 0; i < 3; i++)
{
printf("array[%d]=%d\n",i, *(arr+i));
printf("arrar[%d]=%d\n", i, *(array + i));
}
char s[3][4] = { { "Abc" }, { "Bcd" }, { "Cde" } };//字符串数组其实是一个二维数组
char w[] = { 'A', 'B', 'C' };
char n[] = "abc";
//上面三个全都是在栈区中的,在栈区中放了这些字符。
char* ps[] = {s[0],s[1],s[2]};
/*指针数组才能指向字符串数组,一个单独的指针不能指向一个字符串数组!一个单独的指针只能指向一个字符串,字符串本身就是一个字符数组,所以一个指针变量只能指向一个数组,指向二维数组的应该是指针数组才可以*/
char* pw = w;
char* pn = n;
for (int i = 0; i < 3; i++)
{
printf("s[%d]=%s\n", i, *(s + i));
printf("s[%d]=%s\n", i, *(ps + i));//指针和数组名都有p+1指向下一个元素的用法,因为数组名本身也是一个指针常量
printf("s[%d]=%s\n", i, s[i]);
printf("s[%d]=%s\n", i, ps[i]);
printf("w[%d]=%c\n", i, *(w + i));
printf("w[%d]=%c\n", i, *(pw + i));
printf("n=%s\n", n);
printf("n=%s\n", pn);
printf("n[0]=%c\n", *pn);
}
return 0;
}