上一篇文章:C语言数组+推箱子小游戏
C语言字符数组与字符串+十六进制转十进制+字母和汉字的存储差别+bool型变量+反斜杠代码换行
- 先了解一下字符常量,字符常量很简单,就是用双引号引起来的字符序列,比如“Holle world”。
- 字符串常量“Hello world”代表的是字符串首个字符的内存地址,所以可以用字符串常量直接给字符数组赋值
字符数组与字符串
字符数组的定义与初始化
字符数组和普通的数字数组基本类似。
- 定义:char 数组名 [数组长度]
例如:char ch[10]; 并给它赋值:
ch[0]=‘b’; ch[1]=‘e’; ch[2]=‘i’; ch[3]=‘j’; ch[4]=‘i’; ch[5]=‘n’; ch[6]=‘g’;
如果我只给了部分元素赋值,那么其他元素的值是不确定的。 - 字符数组的初始化:
例如:char[5]={ ‘h’, ‘e’, ‘l’, ‘l’, ‘o’ };
如果只是赋值一部分元素,那么没赋值的元素就会自动赋值‘\0’,即“空操作”,不可显示,也不起任何控制作用,只起一个标志作用。 - 字符数组也可以定义成字符二维数组,同普通的二维数组一样。二维字符数组多用于多个字符串的处理。可以用字符串初始化二维字符数组。
- 字符数组的引用,同普通的数组一样。
#include <stdio.h>
int main()
{
char ch[10]="hello";
for (int i = 0; i < 10; i++)
printf("%c", ch[i]);//putchar(ch[i]);
return 0;
}
- 字符大小写转换
输入20个字符,将其中的小写字母转换为大写字母,如果已经是大写字母或非字母字符,则不做任何操作。转换后输出这些字符。
#include <stdio.h>
int main()
{
char ch[20]="hello";
for (int i = 0; i < 20; i++)//这里的i定义在for循环的内部,它属于for循环的局部变量。
//所以执行到这个for循环的时候,才会给i分配内存空间,当这个for循环结束,i的存储空间就会被回收
ch[i] = getchar();
for (int i = 0; i < 20; i++)//这里再定义一个for循环内部的局部变量,i属于复合语句内的局部变量
{
if (ch[i] >= 'a' && ch[i] <= 'z')
printf("%c",ch[i]-32);
}
return 0;
}
字符串
- 字符串是包含若干字符的字符序列。字符串都是以“\0”为结束标志的
- 字符数组的整体初始化:
char string[10]={"hello"};//将字符常量赋值给字符数组
或
char ch[10]="hello";
等价于
char ch[10]={'H','e','l','l','o','\0'};
“\0”是结束的标志,C语言中,所有对字符串的处理函数,都会忽略“\0”以后的内容。
由于系统在存储字符串常量时,会在串尾自动加上一个结束标志“\0”。
下面看一些细节上的问题:
- 细节一
#include <stdio.h>
int main()
{
char str_1[] = "hello",str_2[5] = "hello";
printf("%d,%d",sizeof(str_1),sizeof(str_2));//求字节长度时使用字符数组的名字,即字符数组的整体引用。
return 0;
}
我定义了两个字符数组,str_1和str_2,str_1没有给它数组长度,str_2给了它数组长度,然后用求字节长度关键字“sizeof”来分别求出它俩所占的内存为多少字节。
结果是第一个字符数组占6个字节,而第二个字符数组占5个字节。
这是因为字符串hello后面还有个\0,\0也占一个字符。而第二个字符数组已经给了数组长度为5,所以最后一个转义字符\0没有空间可存。
sizeof是前面博客中说过的,注意它是一个关键字,而不是函数,它是一个运算符。
- 动态字符串数组如果不初始化,里面会自动存储一些没有意义的值
- 给字符串数组中的每一个元素赋值为0
- 静态数组不初始化会自动赋值为0
- 转义字符 \0 在ASCII码表里面的十进制值为0
- 字符数组的整体引用:
-
- 先注意一个点:数组名是一个常量,存储的是数组的首地址,所以不能给数组名赋值。
-
第一个ch_1正常输出了,因为我给它的存储空间足够大,当我用字符串给他赋值的时候,会把 \0 同时给它,输出的时候系统遇到 \0 后,就停止了;但是ch_2就没有正常输出,因为我给它的存储空间不能存得下hello这个字符串,而它后面的 \0 则不能赋给它,所以输出的时候系统并没有识别到结束标志“\0”,所以系统会在内存中继续访问数组之后的内容,并当作字符输出,直到遇到字符结束标志“\0”或者试图访问没有分配给该程序的内存单元而导致运行出错时为止。
-
- 通过scanf()函数输入字符串
char str[20];
scanf("%s",str);//没有&,因为数组本身就代表了数组的首地址,&是地址运算符
注意:在使用scanf()输入字符串的时候,scanf()函数会自动跳过诸如空格、换行符和制表符等类似的前导空白字符,从第一个非空白字符开始,逐个扫描,并把扫描到的字符逐个复制到字符数组的连续存储空间中。当遇到空白字符时停止扫描,并自动在结尾处存入一个“\0”,停止标志符,即空字符NULL。
puts与gets函数
puts函数和gets函数专门的字符串输出和输入函数。也包含在标准输入输出头文件stdio.h中。
- puts()函数括号内可以加字符数组的名字,表示输出该数组
- puts()函数可以输出空格
- puts()函数中遇到转义字符“\0”时停止输出,即空字符后面的字符不再输出
- 使用puts()函数时,当转义字符 \n 出现在字符中间或结尾时,可以执行换行操作。
- gets()函数从键盘输入字符串,以回车键作为输入结束的标志,注意最后的回车换行\n不会输入进去。使用gets()函数时,对字符串的长度没有限制,所以需要保证字符数组的长度足够用。gets()函数中允许输入空格。
更多常用处理字符串的函数
C语言提供了很多处理字符串的函数,这些函数均包含在string.h头文件中。使用之前必须先#include <string.h>
- strcpy()函数
调用:strcpy(字符数组名,字符串或字符数组);
定义的字符数组长度要尽可能大,至少要比赋给它的字符串的长度大1,这样才能在末尾加上'\0'
-
- 如何解决Visual Studio 2019中strcpy()函数报错的问题
点击Project-------->点击最后一个项目属性
点击C/C++ 然后点击preprocessor 在CONSOLE;
后面加上_CRT_SECURE_NO_WARNINGS;
就可以了。
这个是VS中修改后的运行结果:
(不修改会报错)
- 如何解决Visual Studio 2019中strcpy()函数报错的问题
- strncpy()函数
调用strncpy(字符数组名,字符串或字符数组,n);
将第二个字符串或字符数组的前n个字符复制到第一个字符数组中
- strcat()函数
拼接两个字符串或字符数组
- strncat()函数
调用格式:strncat(字符数组名,字符串或字符数组,n);
将第二个字符串或字符数组的前n个字符拼接到前一个字符数组中
- strcmp()函数
调用:strcmp(字符数组或字符串,字符数组或字符串);
作用是比较两个字符串的大小,比较的是从第一个字符开始往后的字符的ASCII码值
如果前面的字符大于后面的字符,则函数返回值为1
如果前面的字符等于后面的字符,则函数返回值为0
如果前面的字符小于后面的字符,则函数返回值为-1
- strlen()求字符串长度函数
调用:strlen(字符串); 求字符串有效长度,即 \0 之前的字符个数
区别sizeof()求字节数单目运算符,求数组在内存中所占的字节数
- strlwr()函数
将字符串中的大写字母转换为小写字母
在DevC++编译器中strlwr()和_strlwr()都能正常使用
在VS编译器中只能使用_strlwr()
- strupr()函数
将小写字母转换为大写字母函数
在DevC++中_strupr()函数和strupr()函数是一样的,都能用
在VS编译器中只能使用_strupr()函数
汉字与英文字母的储存差别
在内存中,一个英文字母占一个字节,但是一个汉字占两个字节。
字节数不够则会出错。
字节数正好够也会出错,因为没有字符串结束标志 \0
在最后加上字符串结束标志即可正常输出。(注意字符数组第一个元素的下标是从零开始的)
代码案例
- 判断字符是否为回文
#include <stdio.h>
int main()
{
int i, k;
char line[80];
printf("Enter a string:");
k = 0;
while ((line[k] = getchar()) != '\n')
k++; //统计输入字符的个数
line[k] = '\0'; //字符数组从零开始的,第k个元素中没有字符,给它赋值'\0',作为字符串结束标志
i = 0;
k = k - 1;
while (i < k)
{
if (line[i] != line[k])
break;
i++;
k--;
}
if (i >= k)
printf("It is a palindrome\n");
else
printf("It is not a palindrome\n");
return 0;
}
- 判断入学年份、专业、班级以及编号
某学校的专业编号:
计算机专业:01
测控技术专业:02
数学专业:03
英语专业:04
无机非金属专业:05
化工专业:06
高分子专业:07
采矿专业:08
机械专业:09
给排水专业:10
编写程序,输入一个学生的入学编号,判断该学生的入学年份、专业、班级以及编号
例如:2011010112
2011年入学 计算机专业 2班 12号
#include <stdio.h>
#include <string.h>
void main()
{
char sno[20];//学号
char syear[5];//入学年份
char sprof[3];//专业编号
char sclass[3];//班级
char snum[3];//班内编号
char str[100] = "";
int i = 0, l, flag = 0;
printf("请输入一个学号:");
gets(sno);//或scanf("%s",sno);
l = strlen(sno);//求出输入的学号的长度
strncpy(syear,sno,4);
syear[4] = '\0';
strcat(str, "该学生 ");//连接字符串
strcat(str,syear);//连接字符串
strcat(str, "年入学的 ");//连接字符串
for (i = 4; i < 6; i++)
sprof[i - 4] = sno[i];//拆出专业编号
sprof[i - 4] = 0;//此时i的值为6
for (i; i < 8; i++)
sclass[i - 6] = sno[i];
sclass[i - 6] = 0;//此时i的值为8
for (i; i < l; i++)
snum[i - 8] = sno[i];
snum[i - 8] = 0;//此时i的值为10
i = 0;
while (sprof[i] != '\0')//对专业编号里面的字符进行处理,把它转换为十进制数字
{
flag = flag * 10 + sprof[i] - 48;//ASCII码表中,字符‘0’对应的十进制整数为48,所以要减去48
i++;
}
switch (flag)
{
case 1:strcat(str, "计算机专业"); break;
case 2:strcat(str, "测控技术专业"); break;
case 3:strcat(str, "数学专业"); break;
case 4:strcat(str, "英语专业"); break;
case 5:strcat(str, "无机非金属专业"); break;
case 6:strcat(str, "化工专业"); break;
case 7:strcat(str, "高分子专业"); break;
case 8:strcat(str, "采矿专业"); break;
case 9:strcat(str, "机械专业"); break;
case 10:strcat(str, "给排水专业"); break;
};//switch后的这个分号可加可不加
strcat(str,sclass);
strcat(str,"班学生");
strcat(str,"\n班内编号为:");
strcat(str,snum);
puts(str);
}
A+B plus
编写一个程序,输入两个100位的数字,并相加输出。无论是整型还是double型,都无法存储一个100位数的数据,所以可以用字符串来做。
#include <stdio.h>
int main()
{
int d=0,e=0,flag=0,h=0,la,lb;
static int an[101],bn[101],cn[101];
char a[101],b[101];
while((a[d]=getchar())!='\n')
{
d++;
}
la=d;
while((b[e]=getchar())!='\n')
{
e++;
}
lb=e;
for(int i=100;i>0;i--)
{
an[i]=a[la-1]-'0';
la--;
if(la==0)
break;
}
an[0]=0;
for(int i=100;i>0;i--)
{
bn[i]=b[lb-1]-'0';
lb--;
if(lb==0)
break;
}
bn[0]=0;
for(int i=100;i>=0;i--)
{
if(flag==0)
{
if(an[i]+bn[i]>=10)
{
cn[i]=(an[i]+bn[i])%10;
flag=1;
}
else
{
cn[i]=an[i]+bn[i];
flag=0;
}
}
else
{
if(an[i]+bn[i]+1>=10)
{
cn[i]=(an[i]+bn[i]+1)%10;
flag=1;
}
else
{
cn[i]=an[i]+bn[i]+1;
flag=0;
}
}
}
for(int i=0;i<101;i++)
{
if(cn[i]==0)
h++;
if(cn[i]!=0)
break;
}
if(h>100)
printf("0");
else
for(int i=h;i<=100;i++)
printf("%d",cn[i]);
return 0;
}
十六进制转换为十进制
平时生活中我们都是用的十进制,满十进一。记得小时候学数数字,从0数到100,满十进一,十进制是我们从小就开始接触的。
十六进制满十六进一,从0到9,a代表10,b代表11,c代表12,d代表13,e代表14,f代表15。(也可以是大写字母A到F)十六进制的“10”转换为十进制为16。
转换规则:
比如一个十六进制数字:a12,将该十六进制数字从左往右依次拆开,得到2、1、a,a代表的数为10
转换为十进制:2乘以16的0次方+1乘以16的1次方+10乘以16的2次方 = 2*16^0+1*16^1+10*16^2=2578,这个是从左往右处理数字的方法
也可以从右往左处理数字(a*16+1)*16+2=2578,右边第一个数字乘以16+后面一位数字,得到的结果整体再乘以16,然后再加上后面一位数字,一直到最后一位数字为止,比如:
acf2e4
a代表的数值为10,用10乘以16得到160,加上c,c代表的数字是12,160+12=172,172*16=2752,加上f,f代表的数字是15,2752+15=2767,2767*16=44272,44272+2=44274,44274*16=708384,708384+e=708398,e代表的数字是14,708398*16=11334368,11334368+4=11334372,所以最后的结果为11334372
请看转换代码,里面我进行了详细注释,可以判断输入的字符串是否为合法的十六进制数,如果不是,提示再次输入,直到输入正确为止
#include <stdio.h>
int main()
{
int i, flag, j, count;
char hexad[80];
long number;
printf("Enter a string:");//提示输入字符串
do
{
flag = count = 0;
for (i = 0; (hexad[i] = getchar()) != '\n'; i++)
{
count++;
continue;
}
hexad[i] = '0';//这个语句是必要的,为了覆盖最后输入的回车字符,因为getchar()输入会把转义的回车字符也输入进去,这样下面判断是否输入了非十六进制字符时会判断错误
for (j = 0; j <= count; j++)
{
if (hexad[j] >= '0' && hexad[j] <= '9' || hexad[j] >= 'a' && hexad[j] <= 'f' || hexad[j] >= 'A' && hexad[j] <= 'F')//判断是否为合法字符
continue;
else//如果输入了非法字符,提示重新输入,再次进行循环输入
{
flag = 1;
printf("您输入非十六进制的字符,请重新输入!\n");
break;
}
}
} while (flag != 0);
//这个do-while循环的功能是输入一个十六进制数,如果输入了非十六进制的字符,提示重新输入,再输入,直到输入正确为止
hexad[i] = 0;//如果前面输入正确了,再用字符串结束标志0把最后一个字符'0'覆盖掉
//后面的语句是把十六进制数转换为十进制数的语句
number = 0;
for (i = 0; hexad[i] != '\0'; i++)
{
if (hexad[i] >= '0' && hexad[i] <= '9')
number = number * 16 + hexad[i] - '0';//字符零在ASCII码表里面的值是48,所以数字字符的值减去数字字符'0'的值得到该数字字符的数值
else if (hexad[i] >= 'A' && hexad[i] <= 'F')
number = number * 16 + hexad[i] - 'A' + 10;
else if (hexad[i] >= 'a' && hexad[i] <= 'f')
number = number * 16 + hexad[i] - 'a' + 10;
}
//最后把转换好的十进制数输出
printf("Number=%ld\n",number);
return 0;
}
*补充bool型变量和abs()绝对值函数
标题前我加入了星号,表示我对之前博客进行内容补充
- bool型变量
bool型变量使用前必须先包含头文件stdbool.h
bool型变量只有1和0
使用bool型变量时可以用true代替1,用false代替0
while(1)
{
语句;
}
该语句的作用是判断条件一直为真,一直进行while语句中的循环,直到遇到break跳出循环。
也可以写成
#include <stdbool.h>//必须包含此头文件才能用bool型变量中的true代替1
... ...
while(true)
{
语句;
}
- abs()函数
abs()函数是求绝对值函数,但是只针对整型变量,不需要包含math.h头文件
fabs()函数也是求绝对值函数,既可以针对整型变量,也可以针对浮点型变量,但是必须包含math.h头文件
*补充反斜杠代码换行
编写一个代码,如果直接换行的话,是会报错的。代码想要换行的话,要加一个反斜杠,如下:
相当于:
当一个代码语句非常长的时候,就可以用到反斜杠换行。