1.字符串字面量。
"Candy \nIs dandy \nBut liquor \nIs quicker."
在c++中叫字符串字面值.可以在字符串内embed转移序列,例如'\n'
谨慎在字符串中embed八进制和hex转移序列,八进制转移序列以/开始,十六进制转移序列以/x(x必须小写)开始,八进制转移序列在三个数字(必须0~7范围内)之后结束或者以第一个非八进制以内的数字结束(数字大于等于8就结束)。
十六进制/x后面没有限制,以第一个非十六进制数字(大于f)结束。注意十六进制的数字大小写均可(从编译器运行结果得到)。例如看下面代码:
#include <stdio.h>
int main()
{
if(('\xAA') == ('\xaa'))
printf("equal");
else
printf("not equal.");
return 0;
}
运行结果是:
equal
2.延续字符串字面值。
(1)方法1:采用 \
例如 "i am a student \
and you are a teacher. "
特点:第一行以\(不包含)处结束,第二行以行首开始。
(2)方法2:当两条或者多条字符串相邻时候,c语言会自动合并字符串。利用这个规律可以这样书写
"i am a student "
" and you are a teacher. "
最后编译器会自己把字符串合并成。"i am a student and you are a teacher. "
3.字符串字面量的存储。c语言把字符串字面量当成字符数组来存储的。当c语言遇到长度为n的字符串,那么它会为字符串字面量分配长度n+1的内存空间,末尾加'\0'。注意:这里不要混淆'\0'、'0'、' ',空字符的码值为0,而'0'的码值(ASCII)为48,空格' ' ASCll码值是32。切记,如果字符数组默认初始化,那么就是' \0'.
字符数组默认初始化时候,每个字符也是赋值'\0'而不是空格键,算术类型数组初始化每个数字都是0.
空字符串是"",空字符串作为单独的一个空字符存储,'\0'
字符串字面之是当成数组来存储的,所以字符串字面之会被当成是指针(数组名字会被当成指针).例如,printf("abcde");那么会传递给printf函数字符串"abcde"的地址(即指向字符a的内存单元的指针).
4.字符串字面值会被当成是指针。
例如 "abc"
printf("abc");这里“abc”会被当成是指针,指向字母a的内存单元。
printf 和scanf 都接受char *类型作为他的第一个参数。
5.字符串字面值的操作。
(1)字符串字面值不能更改。看下面的代码:
#include <stdio.h>
int main()
{
char *str = "abcde";
*str = 'a';
printf("%s",str);
return 0;
}
运行结果发生运行时错误:
r@r:~/coml_/c/13/2$ gcc 3.c -o 123
r@r:~/coml_/c/13/2$ ./123
Segmentation fault (core dumped)
r@r:~/coml_/c/13/2$
结论:不能更改字符串字面值的内容。但是可以使char *对象指向别的字符串,例如,
#include <stdio.h>
int main()
{
char *str = "abcde";
printf("%s\n",str);
//str 重新指向另一个字符串字面量
str = "efghikasdfasd fasdf asdf asdf asf";
printf("%s",str);
return 0;
运行结果如下:
abcde
efghikasdfasd fasdf asdf asdf asfr
意外的地方:还能超过原来字符串的长度。
(2)一半情况下,可以在需要char *的任何地方使用字符串字面值。
例如,char *p;
p = "abc";这个操作不是拷贝字符串,是把字符a的地址赋值给p,也就是说指针变量p的赋值过程,而不是字符串整体的赋值过程。
(3)指针有下标操作,字符串字面值也有
"abc"[1],指的是字符b
如下函数,把整数转换成十六进制字符的形式。
char number_to_hex(int n)
{
return "0123456789ABCDEF"
}
(4)这里注意十六进制和八进制数和转移序列是不一样的
(a)十六进制数的定义\x00(x大写小写都可以,数字部分没有长度要求),八进制数的定义,\012(数字0开头就是八进制数,\0后面的数字也没有长度限制)
(b)十六进制转义序列必须\x开头(x必须小写,而且后面数字部分最多2位从0~ff),八进制转移序列\开头,后面数字最多3位,看下面的图也能复习下
6.字符串字面值和字符常量不能互换,看下面的代码:
#include <stdio.h>
int main()
{
printf("\n");
printf('\n'); //这里引发错误
return 0;
}
gcc编译器编译结果如下:
r@r:~/coml_/c/13/2$ gcc 4.c -o 123
4.c: In function ‘main’:
4.c:5:8: warning: passing argument 1 of ‘printf’ makes pointer from integer without a cast [-Wint-conversion]
5 | printf('\n');
| ^~~~
| |
| int
In file included from 4.c:1:
/usr/include/stdio.h:332:43: note: expected ‘const char * restrict’ but argument is of type ‘int’
332 | extern int printf (const char *__restrict __format, ...);
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
4.c:5:1: warning: format not a string literal and no format arguments [-Wformat-security]
5 | printf('\n');
7.字符串变量。注意必须以“\0”结尾,否则库函数调用它会出现未定义的行为。
#include <stdio.h>
#define STR_LEN 20
int main()
{
//方法1:直接定义成字符数组+初始化
char str[STR_LEN + 1] = "you are welcome.";
//方法2:直接定义为char * + 初始化
char * str1 = "you are welcome.";
//方法3:先定义一个字符数组,和一个char *,把char *对象指向一个char 数组
char str2[STR_LEN + 1],*str3;
str3 = str2;
//方法4:先定义一个char *,后来可以指向一个字符串字面值
char *ptr;
ptr = "you are welcome";
puts(ptr);
return 0;
}
但是如果要使用scanf读入char*,则必须先开辟足够空间,也就是,必须先声明为 char [STR_LEN + 1]类型,而不能申明为char *类型。
char *p; //仅仅给指针p开辟了足够存放一个指针的空间.而不能给字符串分配空间。如果利用p读入,会引发错误:
#include <stdio.h>
int main()
{
char *p;
scanf("%s",p);
printf("%s",p);
return 0;
}
运行结果如下:按照要求输入了 you aresdf后,输出确是null
you arasdf
(null)
8.初始化字符串变量
c语言处理数组初始化式的时候,当数组初始化式被数组本身短的时候,余下的数组元素会被初始化为0(元素是int,float,double的情况),char 数组遵守同样的规则,会以'\0'补充。
不论给char *,或者给char []类型赋值(值类型是字符串字面量),那么都会自动在结束时候以'\0'补充
#include <stdio.h>
int main()
{
double d[12] = {0.0};
int i;
for(i = 0; i!= 12; ++i)
{
printf("%f\n",d[i]);
}
return 0;
}
~
运行结果如下:
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
9.字符数组和字符指针
相同点:任何接受字符数组或者字符指针的地方,这两个都可以作实际参数。
不同点:
(1)字符数组可以修改某个字符的值,而字符指针不能修改字符串字面值。
(2)字符数组的名字是数组名,而字符指针是变量,可以在执行的时候指向其他字符串。
例如,char *p,这里p是一个指针变量,指向char对象。p的值是可以改变的,仅仅如此(p指向的对象的不能改变,区分*p = ...(不合法), 和p = ...(合法))
char *p;
p = "abcde";//合法,这里更改的是p的值。p是一个变量,当然可以更改了。
*p = "efghi";//不合法,这里要改变p指向的那个字符串字面量,会报错。
10.字符串的读写
(1)读
利用scanf或者fget();
特点:会忽略开始的后面的一切空白,例如输入 a bc def,那么会忽略开始的空格,最后把a读入,后面的一些在内存中缓存,等待后续需要读入的时候。
(2)写
printf("")会严格一个一个地输出,直到遇到第一个'\0'为止,如果字符串末尾没有'\0',会继续写出,直到遇到内存中的第一个'\0'为止。
puts(),会在行末直接加一个'\n'(自动换行)
(3)自己编写读取字符串函数要解决的问题如下
(a)什么时候开始读取(是否忽略开始的空白字符)(b)怎么结束’\n’,空格还是某个字符,结束字符是否要写入字符串 (c)如果读取的字符串太长,那么多余的字符直接忽,还是把它留给下一次的输入操作。
例如,不忽略换行符,在第一个换行符结束,且换行符不存在字符串中,则如下编写,代码中的函数read():
#include <stdio.h>
#define LEN 20
int read(char str[],int n);
void print(char str[],int n);
int main()
{
char array[LEN + 1] = {' '};
read(array,LEN);
print(array,LEN+1);
return 0;
}
int read(char str[],int n)
{
char ch;
char *p = str;
int i = 0;
while((ch = getchar()) != '\n')
{
if(i < n)
{ *p ++ = ch;
i++;
}
}
*p = '\0';
return i;
}
void print(char str[],int n)
{
int i = 0;
for(i = 0; i!= n; ++i)
{
printf("%c",str[i]);
}
}
函数read()也可以如下简单的编写:
int read(char str[],int n)
{int i = 0;
char ch;
while((ch = getchar()) != '\n')
{
if(i < n)
str[i ++] = ch;
}
str[i] = '\0'; //terminates string
return i; //number of characters stored
}
scanf函数和gets函数会在末尾自动加空字符,然而,自己编写函数要自己添加空字符。