C语言字符数组与字符串+十六进制转十进制+字母和汉字的存储差别+bool型变量+反斜杠代码换行

上一篇文章:C语言数组+推箱子小游戏

  • 先了解一下字符常量,字符常量很简单,就是用双引号引起来的字符序列,比如“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中修改后的运行结果:
      在这里插入图片描述
      (不修改会报错)
  • 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头文件

*补充反斜杠代码换行

在这里插入图片描述
编写一个代码,如果直接换行的话,是会报错的。代码想要换行的话,要加一个反斜杠,如下:
在这里插入图片描述
相当于:
在这里插入图片描述
当一个代码语句非常长的时候,就可以用到反斜杠换行。

下一篇文章

编译预处理知识点梳理:宏定义+文件包含+条件编译

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jackey_Song_Odd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值