【C总集篇】第三章 字符串和格式化输入/ 输出


第三章 字符串和格式化输入/ 输出

字符/字符串简要理解

前言

​   C 语言中的字符和字符串是常用的数据类型。字符是一个单个的字母、数字、标点符号或者其他可打印的符号,使用单引号 ’ ’ 表示,例如:‘A’、‘0’、‘+’ 等。C 语言使用 ASCII 码表示字符每个字符都有一个整数值和一个字符表示

​   字符串是字符数组或指针,表示一个或多个字符的序列。字符串使用双引号 " " 表示,例如:“hello”、“world” 等。字符串的最后一个字符是 ‘\0’,表示字符串的结尾。

字符介绍和使用

​   在计算机编程语言中,字符表示单个字符,如字母、数字或标点符号等,用于表示文本信息和各种类型的数据。在C语言中,字符使用单引号(’ ')表示。例如,单引号中的字符‘a’、‘A’、‘0’都是字符。

​   字符变量和字符常量是计算机程序中常用的C字符处理机制。变量通常存储一个或多个字符的系列。常量是字符的固定值,不能被修改。字符常量可以存在程序中或通过用户输入设置,然后被分配给字符变量。

#include "stdio.h"

/*
char c ---------可以理解为 用户自己定义的字符变量
'A'    ---------可以理解为 字符常量
*/

int main() {
	char c = 'A';  // 定义一个字符变量
	printf("The character is %c\n", c); // 输出字符

	char ch = getchar(); // 获取从键盘输入的字符
	printf("The character entered is %c\n", ch);

	return 0;
}

数组的简单介绍

​ 数组可以把数组看作是一行连续的多个存储单元。用更正式的说法是,数组是同类型数据元素的有序序列

所有的数组元素都是在一块连续的地址上的存储的,第一个元素占最低的地址,最后一个元素占最高的地址
在这里插入图片描述

数组的创建格式

​   数组是由数组类型+数组名+数组大小组成的,其中最重要的是数组大小是一个常量表达式

在这里插入图片描述
在这里插入图片描述

/*
此初始化方式第一次看只需知道,等后面正式学到的时候再进一步深入
*/

1int arry[10] = { 0 };//完全初始化
2int arry1[6] = { 1,2,3 };//不完全初始化
3int arry2[5] = { 1,2,3,4,5 };//完全初始化
4char arry3[] = { 'a','b','c' };//完全初始化
5char arry4[] = { 'a','b',66,'c' };//完全初始化
6char arry5[] = "abcdef";//完全初始化
7char arry6[10] = "abc";//不完全初始化

字符串介绍和使用

字符串 --------- 是一个或者多个字符的序列

​   C语言没有专门用于存储字符串的变量类型,字符串都被存储在char类型的数组中,数组是由连续的存储单元构成的,字符串中的字符被存储在相邻的存储单元中,每个单元存储一个字符

​   字符串是字符序列,是C语言中非常重要的一种数据类型。字符串中的字符使用空字符(‘\0’)作为结尾。与字符不同,字符串是必须使用双引号(" ")来表示。在C语言中,我们通常用字符指针【后面学到的】或字符数组来表示字符串。

注意:

  • ​ 双引号不是字符串的一部分。双引号仅告知编译器它括起来的是字符串,正如单引号用于标识单个字符一样。
  • ​ 一般来说,空字符计算机会帮我们自动添加,所以实际使用时只用表明这个是字符串 “ ” 就行了。
    在这里插入图片描述
      因为字符串需要用\0结尾,所以在定义字符串的时候,字符数组的长度要预留多一个字节用来存放\0,空字符不是数字 0,它是非打印字符,其 ASCII 码值是(或等价于)0C中的字符串一定是以空字符结束(’/0‘),这意味着数组的容量必须要比所存储的字符串多1
char name[21];  // 定义一个最多存放20个英文字符或十个中文的数组

/*
*简单理解:
英文字符一般只占用1个位置
而中文字符一般只占用2个位置
*/

在这里插入图片描述## printf

printf函数

  • printf()函数能让用户可以与程序交流它们是输出函数或简称为 I/0函数
  • 可以说他是最常用的输出函数,可以将你写的代码展示到你的显示屏上面,方便调试

  printf函数是格式输出函数,其关键字最末一个字母f即为“格式(format)”之意。其功能是按照用户指定的格式,把指定的数据显示到显示器屏幕上。

printf函数一般格式

printf(格式控制字符串,输出值参数表);

如:

printf("f=%f,c=%f\n",f,c);

其中,f=%f,c=%f\n格式控制字符串f,c输出值参数表

(1)格式控制字符串是用双引号括起来的字符串,包括三类信息:

  • 格式字符。格式字符由“%”引导,如%d、%f等。它的作用是控制输出字符的格式。

  • 转义字符。格式控制字符串里的转义字符按照转义后的含义输出,如上面printf函数双引号内的换行符“\n”,即输出回车。

  • 普通字符。普通字符即需要在输出时原样输出的字符,如上面printf函数中双引号内的“f=”和“c= ”部分。

(2)注意事项

​   输出值参数表是需要输出的数据项的列表,输出数据项可以是常量、变量或表达式,输出值参数之间用逗号分隔,其类型应与格式字符相匹配。每个格式字符和输出值参数表中的输出值参数一一对应,没有输出参数时,格式控制字符串中不再需要格式字符。

注意,如果没有输出参数时,但是还有格式控制字符串,则会发生未知错误。

#include <stdio.h>
#include <string.h>  
int main(void)
{
	int c = 0;
    printf("%d\n%d\n",c);
}

在这里插入图片描述
  这里,第2个号d没有对应任何项。系统不同,导致的结果也不同。不过,出现这种问题最好的状况是得到无意义的值。

printf()的转换说明修饰符

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

printf函数部分格式字符

较为常用已用红框标出的:
在这里插入图片描述

常用格式字符详解

%d
int a = 888,b = -666;
printf("%d\n%d",a,b);

输出结果:

在这里插入图片描述
  还可以在%和格式字符*中间插入格式修饰符*,用于指定输出数据的域宽(所占的列数),如用“%5d”,指定输出数据占5列,输出的数据在域内向右靠齐。如:

%md

例如:

int a = 888, b = -666;
printf("%5d\n%5d", a, b);

在这里插入图片描述
  从结果可以看出,在%和d中间加数字5,888占3个域宽,****指定域宽>输出数据长度**。****输出数据靠右,前面补空格

%f

  输出一个实数(包括单精度、双精度、长双精度),以小数形式输出,有以下几种用法:

  不指定输出数据的长度,由系统根据数据的实际情况决定数据所占的列数。系统处理的方法一般是:实数中的整数部分全部输出,小数部分输出6位

#include <stdio.h>
#include <string.h>  
int main()
{
	float c = 1.11111;
    printf("%f\n",c);
}

[外链图片转存中...(img-fDgeZv9r-1723389211894)]
那怎么控制输出小数点后面的位数呢?

%.mf

#include <stdio.h>
#include <string.h>  
int main()
{
	float c = 1.11111;
    printf("%.3f\n",c);
}

在这里插入图片描述
当然浮点型也可以控制域宽**

%n.mf

#include <stdio.h>
#include <string.h>  
int main()
{
	float c = 11111.11111;
    printf("%30.3f\n",c);
}

在这里插入图片描述
可以看出 %3.f 实际是 %3.0f,小数点前面控制域宽,小数点后面控制小数点保留的个数。**

%c
#include <stdio.h>
#include <string.h>  
int main()
{
	char c ='1';
    printf("%c\n",c);
}
%s
#include <stdio.h>
#include <string.h>  
int main()
{
    printf("%s\n","123312");
}

注意:

请求 printf()函数打印数据的指令要与待打印数据的类型相匹配,否则可能会出现错误
在这里插入图片描述
  第一个printf函数中的输出参数b是double型值58.8,但对应的格式控制符为%d,当类型不一致时并不会进行类型转换,而会将实际转入的double型值当作需要的整形类型来理解,因此出现非预期结果;

​   第二个printf函数中,格式控制字符串给出了两个%引导的格式字符,但是输出参数表中只有一个参数a,参数缺少。因此输出c的值默认为内存中a变量后面存储单元的数据值,c的值无法确定

printf的返回值

返回值是是输出的字符数量,包括数字,字母,标点符号,空格等。

#include <stdio.h>
#include <string.h>  
int main()
{
	int A=43;
	printf("%d\n",printf("%d",printf("%d",A)));
}

最后输出结果是这个
在这里插入图片描述

  代码逻辑:首先,从最内层开始A=43被直接输出。然后,最内层printf的返回值以%d的格式被中间层的printf输出为2。最后,最外层的printf以%d的格式输出中间层的返回值为1。

  其实观察第二个printf的输出值和第三个printf的输出值,第一个printf的输出值和第二个printf的输出值不难发现:
printf的返回值就是输出的字符数量

    第三个printf输出"43"字符数量为2,于是返回值为2,第二个printf就输出"2”
    第二个printf输出"2"字符数量为1,于是返回值为1,第一个printf就输出"1"

#include <stdio.h>
#include <string.h>  
int main()
{
	printf("%d\n", printf("0,1,2,3\n"));
}

在这里插入图片描述
  通过运行结果不难看出,数字0123分别占一个字符,标点符号" , “也是分别占一个字符位,换行符” \n "也是占一个。

#include <stdio.h>
#include <string.h>  
int main()
{
	int num=printf("%d\n", printf("0,1,2,3\n"));
	printf("%d\r\n", num);
}

在这里插入图片描述
num值为2说明num接受printf的返回值是2**

​   也说明了printf输出字符数量是2,其中一个是数字8,另外一个就是printf格式控制中的 " \n "可见,格式控制中的字符也算进返回值哦!

scanf

规则说明

scanf()函数使用指向变量的指针,目前只用记住两条规则:

  • 如果使用scanf()读取基本变量类型的值,在变量前面加个&
  • 如果使用scanf()把字符串存储到字符属数组中,不使用&
#include <stdio.h>
int main(void)
{
    int age;             // variable
    float assets;        // variable
    char pet[30];        // string
    
    printf("Enter your age, assets, and favorite pet.\n");
    scanf("%d %f", &age, &assets); // use the & here
    scanf("%s", pet);              // no & for char array
    printf("%d $%.2f %s\n", age, assets, pet);
    
    return 0;
}

#include <stdio.h>
#define PRAISE "You are an extraordinary being."
int main(void)
{
    char name[40];
    
    printf("What's your name? ");
    scanf("%s", name);
    printf("Hello, %s. %s\n", name, PRAISE);
    
    return 0;
}

注意,scanf它会遇到的第一个空白(空格、制表符或换行符)时就不在输入

所以scanf只会读取字符串中的第一个单词,一般用于输入函数(字符串)的话用fgets()

  • scanf()函数每次读取一个字符,跳过所有的空白字符,直到遇到第一个非空白字符才开始读取。

  • 如果使用字段宽度,scanf()在字段末尾或者第一个空白字符的时候停止读取,无法利用字段宽度让只有一个%s的scanf()读取多个单词,最后要注意一点:当scanf()把字符串放进指定的数组的时候,它会在字符序列的末尾添加上’\0’,让数组中的内容成为一个C字符串。

例如:

/*
scanf(Angle Planins)时 遇到Anglea的时候就已经停止输入
*/

转化说明

转换说明输出
%c字符
%s字符串
%d有符号十进制整数
%u无符号十进制整数
%o无符号八进制整数
%x无符号十六进制整数(小写)
%X无符号十六进制整数(大写)
%f小数形式的浮点数(float)
%lf小数形式的浮点数(double)
%e指数形式的浮点数(小写)
%E指数形式的浮点数(大写)
%g以小数形式和指数形式中宽度较短的形式输出浮点数,并且不输出无意义的0(小写)
%G以小数形式和指数形式中宽度较短的形式输出浮点数,并且不输出无意义的0(大写)
%p地址
%%一个百分号

换说明中的修饰符

转换说明转换说明
*抑制赋值,示例:“%*d"
数字最大字段宽度,输入达到最大字段宽度处,或者第一次遇到空白字符为止,示例:“%10s”
hh把整数作为signed char 或者 unsigned char 类型读取
ll把整数作为long long或者unsigned long long类型读取(C99),示例:“%lld”、“%llu”
h、l或者L“%hd"和”%hi”表明把对应的值存储为short int类型;“%ho”、“%hx"和”%hu"表明把对应的值存储为unsigned short int类型;“%ld"和”%li"表明把对应的值存储为long类型;“%lo”、“lx”和“%lu”表明把对应的值存储为unsigned long类型;“%le"、”%lf“和"%lg"表明把对应的值存储为double类型;在e、f和g前面使用L而不是l,表明把对应的值被存储为long double类型,如果没有修饰符,d、i、o和x表明对应的值被存储为int类型,f和g表明把对应的值存储为float类型
j在整形转换说明后面时,表明使用intmax_t或者uintmax_t类型(C99)示例:“%jd”、“%ju”
z在整形转换说明后面时,表明使用sizeof的返回类型,示例:“%zd”、”%zo"
t在整形转换说明后面时,表明使用表示两个指针差值的类型 ,示例:“%td”、“%tx”

从scanf角度看输入

假设,scanf()根据一个%d转化说明读取到一个整数

  • scanf 函数每次只读取一个字符,先跳过所有非空白字符,知道遇到第一个非空白字符后才开始读取,因为要读取整数,所以如果下一个字符并不是数字或者符号+或-(遇到非数字字符),scanf则会认为已经读到了末尾,然后将这个非数字字符放回输入。这意味着下一次开始读的时候,第一个读到的就是这个数字字符
  • 如果第一个字符不是数字字符的话。scanf则一直无法越过读取下一个字符,因为C规定在第一天字符出错时则停止输入
  • 注意,所有空白的概念是指包含没有空格的特殊情况

scanf的返回值

  • 成功情况:
    • 返回成功读取的项数
  • 读不到任何项如需要读取一个数字但是前面是个非数字类型的字符
    • 返回0
  • 读到文件结尾
    • 返回EOF,这是stdio.h中定义的特殊值,通常用#define定义成-1
scanf("%d %d",&a,&b);

函数返回值为int型。如果a和b都被成功读入(输入都是数字),那么scanf的返回值就是2;

如果只有a被成功读入(其中一个是数字),返回值为1;

如果a和b都未被成功读入(例如输入q,2.5等字符),返回值为0;

如果遇到错误或遇到end of file,返回值为EOF。end of file相当于Ctrl+z 或者Ctrl+d。

putchar

输出

putchar() → 字符数据输出

输出字符数据是可以使用 putchar() 函数的,其作用是向显示设备进行输出①个无符号字符。

注意:是①个字符,当然也可以说是单个字符咯。

标准格式:

int putchar(int character);

▪️ 目的:
  putchar 将一个字符输出到标准输出(通常是控制台)。

▪️ 参数:
  character:要输出的字符。它以 int 类型传递,但只使用最低有效的 8 位,因此实际上被视为 char 类型处理。

▪️ 返回值:
  将写入的字符作为 unsigned char 类型返回,如果发生错误,则返回 EOF。

▪️用法:
  可以使用 putchar 在控制台(屏幕)上显示字符。

#include <stdio.h>
 
int main() {
    char ch = 'A';
    printf("显示一个字符:");
    putchar(ch); //putchar接收到一个参数(ASCII码值),将字符输出到标准输出
    return 0;
}

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main(void)
{
	int a = 'A';
	putchar(a);
	return 0;
}

运行结果:A

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main(void)
{
	int a = 65;
	putchar(a);
	return 0;
}

运行结果:A

注意:用putchar()输出整形变量会转换成对应ASCll码的值。(十进制转换成字符)

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define num 65
#define c 'A'
int main(void)
{
	putchar(num);
	putchar(c);
	return 0;
}

运行结果🖊:AA

除了上面的③种情况,putchar()还可以输入转义字符的

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main(void)
{
    /* \101是八进制 */
	putchar('\101');
	return 0;
}

运行结果🖊:A
在这里插入图片描述
那么无论是那种进行输出我们都不难发现最终输出的都会是字符,无论是转没转换亦如此。

返回值

  • 如果成功,则返回所写的字符。如果发生写错误,就返回EOF并设置错误指示器(ferror)
  • C语言标准函数库中表示文件结束符。

通常,EOF 定义在stdio.h文件中:

#define EoF(1)

为什么是-1?

​   因为 getchar()函数的返回值通常都介于0 ~ 127,这些值对应标准字符集。但是,如果系统能识别扩展字符集,该函数的返回值可能在0~255之间。无论哪种情况,-1都不对应任何字符,所以,该值可用于标记文件结尾。

注意点:
在这里插入图片描述

getchar

概念

getchar()----读取单个字符的函数

注意:此时是读取单个字符

int  getchar (void)

1、getchar其实返回的是字符的ASCII码值(整数)。
2、getchar在读取结束或者失败的时候,会返回EOF。

注意:EOF意思是end of file,本质上是-1.

连续单个字符串

#include <stdio.h>
#include <string.h>
int main()
{
	int ch = 0;  //因为 getchar() 返回类型为 int
	while ((ch = getchar()) != EOF) // 连续输入单个字符
	{
		printf("%c",ch);  // 输出一个字符
		//putchar(ch);    // 此时 printf("%c",ch) 与  putchar(ch)  输出结果一样
  	}
 
	return 0;
}

解析:getchar先读取一个字符放到ch里面去,如果这个字符不等于EOF,就进入循环,打印这个字符。当getchar读到文件末尾或者结束时,它会返回一个EOF,此时结束循环。

注意: printf(“%c”,ch) 与 putchar(ch) 输出结果一样

注意:如果想要结束连续输入 输入:ctrl+z 即可
在这里插入图片描述

浅析getchar与scanf的区别

1.getchar 作用是从键盘读入字符,每次只读取一个,一次读入。只有当遇到回车键时才会结束读取。且getchar有类型,为int型,所以getchar的输入一般为int类型。

2.scanf的作用是输入单个字符。但是当scanf遇到空格时就结束读取,不能输入\n。尽管后面还有其他字符。如果要输入长字符,需要用到%s这个格式控制符。

在用函数scanf()输入数值型数据时遇到以下几种情况都认为数据输入结束:

  1、遇到空格符、回车符、制表符(tab);

  2、达到输入域宽;

  3、遇到非法字符输入。

如以下例子:

如输入a的值为k,则当回车时输出字符为k。

#include<stdio.h>
int main ()
{
    char a;
    scanf("%c",&a);
    printf("%c",a);
    return 0;
}

若输入abc分别为kg(空格)h,则输出的结果为kg(空格)。

#include<stdio.h>
int main()
{
    char a,b,c;
    scanf("%c%c%c",&a&b&c);
    printf("%c%c%c",a,b,c);
    return 0;
}

对于getchar来说

#include<stdio.h>
int main()
{
	char x;
	while(x!='h')
	{
		x=getchar();
	}
	putchar(x);
	return 0;
}

当输入gkh时,结果输出h。

const 限定符

const用于限定一个变量为只读

简单理解什么叫只读:不可更改的值
后续再深入理解

sizeof

sizeof是计算变量在内存的占空间的大小,单位是字节

需要注意的点

  • sizeof操作符返回一个size_t类型的值,代表给定对象的大小(以字节为单位)。
  • 可以使用sizeof获取基本数据类型(如int、char)的大小,以及数组、结构体、联合体和指针的大小。
  • sizeof是在编译时计算的,它并不对实际执行时的值进行评估。它根据类型或表达式的编译时类型来确定大小。
  • 对于指针,sizeof返回指针本身的大小,而不是指针指向的内存空间的大小。
  • 对于数组,sizeof返回整个数组所占的内存空间大小,而不是数组元素的个数。
  • sizeof并不会求解指针所指向的内存的大小。例如,对于一个动态分配的数组或字符串,sizeof操作符不会告诉你它所占的实际空间

查看数据类型占空间大小

# include <stdio.h>
# include <string.h>


int main()
{
	printf("sizeof(char): %d\n", sizeof(char));
	printf("sizeof(short): %d\n", sizeof(short));
	printf("sizeof(int): %d\n", sizeof(int));
	printf("sizeof(long): %d\n", sizeof(long));
	printf("sizeof(long long): %d\n", sizeof(long long));
	printf("sizeof(float): %d\n", sizeof(float));
	printf("sizeof(double): %d\n", sizeof(double));
	return 0;
}

执行后的结果

sizeof(char): 1
sizeof(short): 2
sizeof(int): 4
sizeof(long): 4
sizeof(long long): 8
sizeof(float): 4
sizeof(double): 8

计算基本数据类型变量的占用空间的大小

# include <stdio.h>
# include <string.h>


int main()
{
	char c = 'a';
	int i = 1;
	short s = 1;
	long l = 1;
	long long ll = 1;
	float f = 1.0;
	double d = 1.0;
	printf("sizeof(c): %d\n", sizeof(c));
	printf("sizeof(s): %d\n", sizeof(s));
	printf("sizeof(i): %d\n", sizeof(i));
	printf("sizeof(l): %d\n", sizeof(l));
	printf("sizeof(ll): %d\n", sizeof(ll));
	printf("sizeof(f): %d\n", sizeof(f));
	printf("sizeof(d): %d\n", sizeof(d));
	return 0;
}

执行后的结果

sizeof(c): 1
sizeof(s): 2
sizeof(i): 4
sizeof(l): 4
sizeof(ll): 8
sizeof(f): 4
sizeof(d): 8

用于计算数组长度

​   使用sizeof计算数组时,结果为数组长度*数组元素占用空间的大小,当数组为字符数组时,sizeof的功能是一致的,

​   只不过当使用字符数组定义字符串的时候,对于字符串比如hello,数组中会自动增加一个元素’\0’,所以sizeof计算的结果为6

​   而strlen是计算字符串长度的,遇到’\0’结束,但是不把’\0’计算在内因此strlen计算的结果为5

​   这与我们感觉上的字符串的长度为5的感觉是一致的,这就是strlen和sizeof在计算字符数组的时候的区别

# include <stdio.h>
# include <string.h>


int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
	printf("sizeof(arr): %d\n", sizeof(arr));
	char arr2[] = "hello";
	printf("sizeof(arr2): %d\n", sizeof(arr2));
	printf("strlen(arr2): %d\n", strlen(arr2));
	return 0;
}

执行后的结果

sizeof(arr): 40
/*
sizeof(int): 4
数组个数为10
所以
	数组长度*数组元素占用空间的大小 = 4*10
*/    
sizeof(arr2): 6    
strlen(arr2): 5

2

#include <stdio.h>
#include <string.h>  
#define PRAISE "You are an extraordinary being."    
int main(void)
{
    char name[40]={0};
    
    printf("What's your name? ");
    scanf("%s", name);
    printf("Hello, %s. %s\n", name, PRAISE);
    printf("Your name of %d letters occupies %d memory cells.\n",
           strlen(name), sizeof name);
    printf("The phrase of praise has %zd letters ",
           strlen(PRAISE));
    printf("and occupies %zd memory cells.\n", sizeof PRAISE);
    
    return 0;
}

执行后的结果

在这里插入图片描述
sizeof运算符报告,name数组有40个存储单元。

​ 但是,只有前3个单元用来储存 serendipity,所以 strlen()得出的结果是 3。

​ name数组的第4个单元储存空字符,strlen()并未将其计入。

计算指针的占用空间大小

​   这里需要注意的是,32位平台所有类型的指针的占用空间大小都是4个字节,64位平台所有类型的指针占用的空间大小为8个字节

#include <stdio.h>


int main()
{
	printf("sizeof(char*): %d\n", sizeof(char*));
	printf("sizeof(short*): %d\n", sizeof(short*));
	printf("sizeof(int*): %d\n", sizeof(int*));
	printf("sizeof(long*): %d\n", sizeof(long*));
	printf("sizeof(long long*): %d\n", sizeof(long long*));
	printf("sizeof(float*): %d\n", sizeof(float*));
	printf("sizeof(double*): %d\n", sizeof(double*));
	return 0;
}

在32位下执行结果如下:

sizeof(char*): 4
sizeof(short*): 4
sizeof(int*): 4
sizeof(long*): 4
sizeof(long long*): 4
sizeof(float*): 4
sizeof(double*): 4

在64位下执行结果如下:

sizeof(char*): 8
sizeof(short*): 8
sizeof(int*): 8
sizeof(long*): 8
sizeof(long long*): 8
sizeof(float*): 8
sizeof(double*): 8

strlen

​   计算的是字符串str的长度,从字符的首地址开始遍历,以 ‘\0’ 为结束标志,然后将计算的长度返回,计算的长度并不包含’\0’。

size_t  strlen (const char* str);
  1. 函数的参数为------const char* str:字符指针
  2. 返回值的类型------size_t:无符号整数(即:unsigned int)

需要注意的点

  • strlen函数接受一个指向字符串的指针(const char *str)作为参数,并返回一个size_t类型的值,代表字符串的长度。
  • strlen通过逐个检查字符,直到遇到NULL字符(‘\0’),来确定字符串的长度。
  • strlen仅适用于以NULL字符结尾的C字符串(字符数组),它无法直接计算非以NULL结尾的字符串的长度。
  • 如果传递给strlen的指针是空指针(NULL),将导致未定义行为。因此,在使用strlen前应确保指针有效。

计算字符串个数

char* str1 = "hello";    // 5,注意末尾有个隐藏的'\0'
 
char str2[] = "hello";   // 5
 
char str2[] = {'h','e','l','l','o','\0'};   // 5
 
char str2[] = {'h','e','l','l','o','\0','a','b','c'};   // 5,一遇到'\0',函数就会返回
 
char str2[] = {'h','e','l','l','o'};   // 长度不确定,因为没有'\0',因为数组不会自动补充

以下两行代码

//场景一
#include <stdio.h>
#include <string.h>
int main()
{
	char arr[10] = "abcde";
	int num = strlen(arr);
	printf("数组arr的长度为:%d\n", num);
 
	return 0;
}
//场景二
#include <stdio.h>
#include <string.h>
 
int main()
{
	char arr[] = { 'a','b','c','d','e' };
	int num = strlen(arr);
	printf("数组arr的长度:%d\n", num);
 
	return 0;
}

strlen函数:当计算长度时,只有遇到’\0’才会停止计算,同时计算的长度不包含’\0’。

场景一,arr字符数组中存储的是一个字符串(字符串是以'\0'为结束标志的),那么strlen遍历到字符 'e' 时,再向后遍历,就会遇到'\0',此时strlen停止遍历,返回字符个数:5;

场景二:字符'a'、'b'、 'c' 、'd' 、'e'五个字符依次存储在arr的字符数组中,并没有存储'\0',所以'e'字符后面存储的内容我们并不知道有什么。而strlen函数只有遇到'\0'时才停止,所以返回的个数是一个随机值。

故:我们使用strlen函数时,应该检查字符数组是否以’\0’为结束标志。

define 宏定义

赋值

#define MAX 1000	//为 MAX,创建一个简短的名字

意思就是给MAX赋值为1000

​ 可以用代码使用宏定义,同样也可以定义数组的大小

#define MAX 1000
#include<stdio.h>
int main()
{
	printf("%d\n", MAX);
    int arr[MAX];
	return 0;
}

在这里插入图片描述
  我们在编译器中可以看到左边的MAX都被替换成了1000,也就是说在以后的代码中可以用到1000的地方,都可以使用MAX来表示

定义关键字

#define reg register      //为 register这个关键字,创建一个简短的名字

如果觉得register太长了,使用不方便,我们还可以创一个简单的名字reg
在这里插入图片描述
可以看到 reg被替换成了register。

用更形象的符号来替换一种实现

#define do_forever for(;;)

我们都知道如果for循环中什么条件都没有那就是死循环

所以如果我们想要写出死循环的话也可以使用这种方法
在这里插入图片描述

宏定义常量

C 语言中,可以用 #define 定义一个标识符来表示一个常量

//定义常量
#define A 100                   //定义整型变量A值为100
#define B "Hello"               //定义字符串变量B值为"Hello"(定义字符串需要加双引号)
#define C 3.1415926        		//定义浮点数变量C值为3.1415926

​   凡是以 # 开头的均为预处理指令,预处理又叫预编译。预编译不是编译,而是编译前的处理。这个操作是在正式编译之前由系统自动完成的

定义函数

定义不带参数的函数
#include <stdio.h>
 
#define A (x*2+5)
 
int main(void)
{
    int x = 2;
 
    printf("A是:%d\n",A);
 
    return 0;
}
 
/*
A是:9
*/
定义带参数的函数
#include <stdio.h>
 
#define A(x) (x*2+5)
 
int main(void)
{
 
    printf("A是:%d\n",A(2));
 
    return 0;
}
 
/*
A是:9
*/
#include <stdio.h>
 
#define MAX(a,b) (a>b)?a:b  //取两个数最大值
#define MIN(a,b) (a<b)?a:b  //取两个数最小值
 
int main(void)
{
    printf("最大值是:%d\n",MAX(5,100));
    printf("最小值是:%d\n",MIN(5,100));
    return 0;
}
 
/*
最大值是:100
最小值是:5
*/

在这里插入图片描述

define定义函数需注意陷阱

#include <stdio.h>
 
#define A(x) x*x
 
int main(void)
{
    printf("A是:%d\n",A(2));    //2*2=4
    printf("A是:%d\n",A(2+1)); //2+1*2+1=5
    return 0;
}
 
/*
A是:4
A是:5
*/
#include <stdio.h>
 
#define A(x) (x)*(x)
 
int main(void)
{
    printf("A是:%d\n",A(2));    //(2)*(2)=4
    printf("A是:%d\n",A(2+1)); //(2+1)*(2+1)=9
    return 0;
}
 
/*
A是:4
A是:9
*/
#include <stdio.h>
 
#define A(x) (x)*(x)
int main(void)
{
    printf("A是:%d\n",A(2));      //(2)*(2)=4
    printf("A是:%d\n",9/A(2+1)); //9/(2+1)*(2+1)=9
    return 0;
}
 
/*
A是:4
A是:9
*/
#include <stdio.h>
 
#define A(x) ((x)*(x))
int main(void)
{
    printf("A是:%d\n",A(2));      //((2)*(2))=4
    printf("A是:%d\n",9/A(2+1)); //9/((2+1)*(2+1))=1
    return 0;
}
 
/*
A是:4
A是:1
*/

在这里插入图片描述

#include <stdio.h>
#include <string.h> 

#define MAX(X,Y)  ((X)>(Y)?(X):(Y))
int main()
{
    int a=3;
    int b=5;
    int c=MAX(a++,b++);
    //int c=((a++)>(b++)?(a++):(b++));
    printf("%d\n",a);
    printf("%d\n",b);
    printf("%d\n",c);
}

在这里插入图片描述
那我们依次对其进行运算,第一次a++结果是4,第一次b++结果是6,第二次a++结果是5,第二次b++结果是7**

define替换的规则

最后我们不妨再了解一下#define替换的规则

在程序中扩展#define定义符号和宏时,需要涉及几个步骤。

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

  也就是说#define宏定义会不断地检查我们所使用的地方并且不断地替换宏定义的内容,直到所有内容都被替换完为止。

注意事项
  我们在使用**#define定义后不能添加分号**,很多人敲代码时都会习惯性的在后面加上分号,当然这种习惯不能用在宏定义中,使用时请注意

#和##

这个#并不是#define中的#,而是另一种意义的操作符,把一个宏参数变成对应的字符串。

#include <stdio.h>
#include <string.h>  
int main()
{
    int a=10;
    printf("the value of a is %d\n",a);
 
    int b=20;
    printf("the value of b is %d\n",b);
 
    float f=3.14f;
    printf("the value of a is %f\n",f);
}

在这里插入图片描述
可以看到第一种方式是我们平时使用的输出方式,将输出内容放在打印函数的双引号内即可被输出;

​   那第二种方式也可以用来输出内容,可以将使用双引号引用我们想打印的内容,结果和第一种是相同的,这也是字符串的一个特点。

  我们现在知道了这个特征,我们就可以对上面那段冗余的代码进行操作了,直接上代码

#include <stdio.h>
#include <string.h>  
#define print_format(num,format) \
            printf("the value of "#num" is "format,num)
int main()
{
    int a=10;
    print_format(a,"%d\n");
 
    int b=20;
    print_format(b,"%d\n");
 
    float f=3.14f;
    print_format(f,"%f\n"); 
    return 0;
}

在这里插入图片描述

##

##可以把位于它两边的符号合成一个符号

int henghengaaa=114514;
#define CAT(x,y) x##y
 
int main()
{
    printf("%d\n",CAT(hengheng,aaa));
    return 0;
}

在这里插入图片描述
  可以看到我们通过#define 的宏定义将字符串的内容连接在了一起,并且成功的输出了我们预期中的结果。

  当然我们合成的前提是我们合成的符号必须是可以用的,如果合成出来的字符串没有出现过,那也不能正常使用。

  • 19
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是满足要求的C++代码: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; const int MAXN = 200; struct Node { int sum; // 子集和 vector<int> path; // 子集 }; int n, m; int a[MAXN + 5]; bool vis[MAXN + 5]; bool cmp(Node a, Node b) { return a.sum < b.sum; } void dfs(int u, int sum, vector<int> path, vector<Node>& ans) { if (sum > m) return; // 剪枝 if (u > n) { if (sum == m) { Node node = {sum, path}; ans.push_back(node); // 加入答案 } return; } dfs(u + 1, sum, path, ans); // 不选当前数 path.push_back(u); dfs(u + 1, sum + a[u], path, ans); // 选当前数 } int main() { cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> a[i]; } vector<int> path; vector<Node> ans; dfs(1, 0, path, ans); if (ans.empty()) { cout << "no solution!\n"; } else { sort(ans.begin(), ans.end(), cmp); for (int i = 0; i < ans.size(); i++) { for (int j = 0; j < ans[i].path.size(); j++) { cout << ans[i].path[j] << " "; } cout << "\n"; } } return 0; } ``` 解释一下代码的思路: - 首先输入总集规模n和子集和数M,以及n个正整数a[i]。 - 然后定义一个节点结构体Node,包含子集和sum和子集path(用vector表示)。 - 定义一个dfs函数,其中u表示当前考虑到第几个数,sum表示当前子集的和,path表示当前子集的路径,ans表示答案集合。 - 在dfs函数中,先判断是否超过子集和,如果超过则剪枝;如果已经考虑完了所有数,判断当前子集和是否为M,如果是则将当前子集加入答案集合。 - 接着分别考虑选或不选当前数,进行递归搜索。 - 最后在主函数中调用dfs函数,如果答案集合为空,输出"no solution!";否则对答案集合进行排序,依次输出每个子集的路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值