news 2026/4/15 14:52:57

C 语言学习历程:(第二章)数据类型和变量・程序的 “数据骨架”

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C 语言学习历程:(第二章)数据类型和变量・程序的 “数据骨架”

第二章:数据类型和变量


文章目录

  • 第二章:数据类型和变量
    • 1. 数据类型及长度和取值范围
      • 1.1 字符型
      • 1.2 整形
      • 1.3 浮点型
      • 1.4 布尔类型
      • 1.5 sizeof
      • 1.6 取值范围
    • 2.变量
      • 2.1 变量命名规则:
      • 2.2 变量分类
    • 3. 操作符
      • 3.1 算数操作符
      • 3.2 赋值操作符
      • 3.3 单目操作符
    • 4. 强制类型转换
    • 5. printf 和 scanf
      • 5.1 printf
        • 5.1.1 占位符
        • 5.1.2 限定最小宽度:
        • 5.1.3 限定小数位数:
        • 5.1.4 输出部分字符串:
      • 5.2. scanf
        • 5.2.1 基本用法
        • 5.2.2 返回值
        • 5.2.3 占位符
        • 5.2.4 赋值忽略符
        • 5.2.5 printf 和 scanf 差异
  • 声明

1. 数据类型及长度和取值范围

  • signed 关键字,表示⼀个类型带有正负号,包含负值
  • unsigned 关键字,表示该类型不带有正负号,只能表示零和正整数。好处是同样长度的内存能够表示的最大整数值增大一倍。

计算机存储的是二进制 0 和 1,存放一个 0 或者 1 需要一个比特位,一个字节为八个比特位

1.1 字符型

  • char:1个字节
  • [signed] char
  • unsigned char

C 语言规定char类型默认是否带有正负号,由当前系统决定。
一般默认char等同于signed char

1.2 整形

短整型:

  • short [int]:两个字节
  • [signed] short [int]
  • unsigned short [int]

整形:

  • int:4个字节
  • [signed] int
  • unsigned [int]

长整形:

  • long [int]:4个字节
  • [signed] long [int]
  • unsigned long [int]

长长整形(C99引入)

  • long long [int]:8个字节
  • [signed] long long [int]
  • unsigned long long [int]

1.3 浮点型

  • float:4个字节
  • double:8个字节
  • long double:在 vs 上是8个字节,gcc 是16个字节

1.4 布尔类型

在 C 语言中 0 表示假,非零即为真。布尔类型的变量取值是:truefalse

  • bool或者_Bool

1.5 sizeof

sizeof操作符专门用来计算类型长度,单位是字节,计算结果是size_t类型。sizeof操作符的操作数可以是类型,也可以是变量或者表达式(表达式不参与计算)。

#include<stdio.h>// 标准输入输出的头文件intmain(){// sizeof 不是类型的时候可以省略括号inta=99;printf("%zd\n",sizeof(a));// sizeof 打印应该用 %zd 格式,sizeof函数的返回值类型是 size_tprintf("%zd\n",sizeofa);printf("%zd\n",sizeof(int));printf("%zd\n",sizeof(2+5));// sizeof的表达式不实际运算,而是根据类型进行推断// 验证 sizeof表达式不实际计算shortb=3;printf("%zd\n",sizeof(b=10+b));// 输出 2,类型是由 b 决定printf("b = %d\n",b);// b = 3,由此可见 sizeof 表达式不进行计算printf("%zd\n",sizeof2+5);// 表达式省略括号后,先计算 2 的长度是 4 ,接着 + 5 = 9// 数据类型长度printf("%zd\n",sizeof(char));// 字符型 1printf("%zd\n",sizeof(bool));// 布尔型 1printf("%zd\n",sizeof(short));// 短整型 2printf("%zd\n",sizeof(int));// 整型 4printf("%zd\n",sizeof(long));// 长整型 4 C 语言标准规定 long >= intprintf("%zd\n",sizeof(longlong));// 长长整形 8printf("%zd\n",sizeof(float));// 单精度浮点型 4printf("%zd\n",sizeof(double));// 双精度浮点型 8printf("%zd\n",sizeof(longdouble));// 长双精度浮点型 8return0;}

1.6 取值范围

每一种数据类型都有自己的取值范围,也就是存储数值的最小值和最大值的区间。

  • CHAR_MINCHAR_MAX:char 的最小值和最大值
  • SHRT_MINSHRT_MAX:shot 的最小值和最大值
  • INT_MININT_MAX:int 的最小值和最大值。
  • LONG_MINLONG_MAX:long 的最小值和最大值
  • LLONG_MINLLONG_MAX:long long 的最小值和最大值
  • UCHAR_MAX:unsigned char 的最大值
  • USHRT_MAX:unsigned short 的最大值
  • UINT_MAX:unsigned int 的最大值
  • ULONG_MAX:unsigned long 的最大值
  • ULLONG_MAX:unsigned long long的最大值

2.变量

  • 数据类型 变量名

变化的值称为变量,不变的值称为常量。创建变量的本质就是向内存中申请一块空间,并将变量的值存储在空间中。

2.1 变量命名规则:

  • 只能由字母(大小写)、数字和下划线 _ 组成
  • 不能以数字开头
  • 长度不超过63个字符
  • 变量名区分大小写
  • 不能使用关键字

2.2 变量分类

  • 全局变量:大括号外部定义的变量,在整个工程中都可以使用
  • 局部变量:在大括号内部定义的变量,只能在自己所在的局部范围使用
#include<stdio.h>// 标准输入输出的头文件// 全局变量,整个工程都可以使用intpublic;floatlocal=99.9f;//定义变量时给初始值叫初始化intmain(){//局部变量:代码块内声明,只能在本代码块使用floatlocal=23.6f;// 小数默认为 double ,所以末尾加 f 表明 float 类型printf("%f\n",local);// 全局变量与局部变量同名,优先局部变量return0;}

全局变量和局部变量在内存中如何存储呢?

学习 C 语言的过程中,需要用到内存中的三个区域:栈区,堆区,静态区

  • 栈区:局部变量和函数参数
  • 堆区:动态内存管理
  • 静态区:动态内存管理

为什么变量要初始化呢?

  • 局部变量不初始化,他的值是随机的
  • 全局变量不初始化,他的值会默认初始化为0

3. 操作符

3.1 算数操作符

+ - * / %这些操作符都需要两个操作数,也被称为双目操作符。

注意:

/:两端若是整数,执行的是整除,得到的也是整数。如果希望执行浮点数除法,两个运算数必须至少一个为浮点数。

%:只能用于整数,不能用于浮点数,负数求模结果的正负号则有第一个运算数的正负号决定。

实操:

#include<stdio.h>// 标准输入输出的头文件intmain(){printf("%d\n",6+4);printf("%d\n",4-6);printf("%d\n",6*4);intx=6/4;// 两端整数执行的是整除,得到的也是整数,丢掉小数部分printf("%d\n",x);// 1floaty=6.0/4;// 只要一端为小数,执行的便是浮点数除法printf("%f\n",y);// 1.500000// 求模只能用于整数,不能有一方是浮点数printf("%d\n",6%4);//负数求模,结果的正负号由第一个运算数的正负号决定printf("%d\n",-6%4);// -2printf("%d\n",6%-4);//2printf("%d\n",-6%-4);//-2return0;}

3.2 赋值操作符

  • 初始化:创建变量时给值
  • 赋值:变量创建好后再给一个值

复合赋值符:

  • +=
  • -=
  • *=
  • /=
  • %=
  • >>=
  • <<=
  • &=
  • |=
  • ^=

实操:

#include<stdio.h>// 标准输入输出的头文件intmain(){//复合赋值符inta1=10;printf("%d\n",a1+=3);// a1 = a1 + 3 13printf("%d\n",a1-=3);// a1 = a1 -3 10printf("%d\n",a1*=3);// a1 = a1 * 3 30printf("%d\n",a1/=3);// a1 = a1 / 3 10printf("%d\n",a1%=3);// a1 = a1 % 3 1return0;}

3.3 单目操作符

顾名思义只有一个操作数:

  • ++:自增

    1. ++ 前置:先+1,后使用
    2. 后置 ++:先使用,再 +1
  • --:自减

    1. – 前置:先 -1,再使用
    2. 后置 --:先使用,再 -1
  • +:正号

  • -:负号

实操:

#include<stdio.h>// 标准输入输出的头文件intmain(){intc1=10;intd1=++c1;//前置++,先自增,再使用 :c1 = c1 + 1 d1 = c1printf("c1:%d\n",c1);// 11printf("d1:%d\n",d1);// 11intc2=10;intd2=c2++;//后置++,先使用,再自增 :d2 = c2 c2 = c2 + 1printf("c2:%d\n",c2);// 11printf("d2:%d\n",d2);// 10intc3=10;intd3=--c3;//前置--,先自减,再使用 :c3 = c3 - 1 d3 = c3printf("c3:%d\n",c3);// 9printf("d3:%d\n",d3);// 9intc4=10;intd4=c4++;//后置--,先使用,再自减 :d4 = c4 c4 = c4 - 1printf("c4:%d\n",c4);// 9printf("d4:%d\n",d4);// 10intc5=10;intd5=-c5;printf("c5:%d d5:%d\n",c5,d5);//多个占位符输出,后面内容与占位符一一对应,如果少于则会输出内存中的任意值//int ch = getchar();// 此函数仅接受字符,输入 2 的时候是字符 2 ,函数返回的是 ASCALL 码值//printf("%d\n", ch);//putchar(ch);// putchar输出字符 = printf("%c"),函数接受字符或者 ASCALL码}

4. 强制类型转换

(类型)

#include<stdio.h>// 标准输入输出的头文件intmain(){floatx1=(float)(6/4);// int 强转为 float ,小数点后补 6 个 0//或者 float x1 =6 / 4; 底层默认就会转换printf("%f\n",x1);// 1.000000printf("%f\n",6/4);// 不能通过占位符强转,int 类型使用 %f 占位符结果为 0.000000inty2=(int)(6.0/4);// double 强转为 int,丢掉小数部分printf("%d\n",y2);// 1printf("%d\n",99.9/3);// 不能通过占位符强转,float 类型使用 %d 占位符结果为随机值return0;}

5. printf 和 scanf

5.1 printf

5.1.1 占位符

函数里的f代表format(格式化),表示可以定制输出文本的格式,所以就要用到占位符。

常用占位符列举:

  • %c:char
  • %f: float 和 double
  • %lf:double
  • %Lf:long double
  • %d:int
  • %u:unsigned int
  • %hd:short int
  • %hu:unsigned short int
  • %ld:long int
  • %lu:unsigned long int
  • %lld:long long int
  • %p:指针
  • %s:字符串
  • %x:十六进制整数
  • %zd:size_t

占位符会被后边的值所替换。多个占位符时,后面内容需要与占位符一一对应,如果少于则会输出内存中的任意值。

#include<stdio.h>// 标准输入输出的头文件intmain(){printf("hello world\n");// \n 换行符,printf("%s\n","hello world");//%s 字符串占位符printf("%c\n",'a');// %c:字符占位符,'':字符,只能包含一个字符printf("%d\n",123);// %d:整数占位符printf("%f\n",3.14);// %f:float 单精度浮点型和 double 双精度浮点型占位符,默认小数点后 6 位,不足补零printf("%lf\n",3.14);// %lf:double 双精度浮点型占位符,默认小数点后 6 位,不足补零printf("%Lf\n",3.14);// %Lf:long double 长双精度浮点型占位符,默认小数点后 6 位,不足补零printf("%s %d\n","hello world",99);//多个占位符时,后面内容需要与占位符一一对应return0;}
5.1.2 限定最小宽度:
  • %[m]占位符:限定数据的最小宽度
#include<stdio.h>// 标准输入输出的头文件intmain(){printf("%d\n",123);printf("%+d\n",+123);//显示正负号号,正号默认不显示,%+d 显示printf("%+d\n",-123);// 指定整数最小宽度,默认右对齐(前面补零)printf("%5d\n",123);printf("%-5d",123);// 左对齐(后面补零)printf("hehe\n");// 可以看到左对齐的效果return0;}
5.1.3 限定小数位数:
  • %.[m]f:指定小数部分位数
#include<stdio.h>// 标准输入输出的头文件intmain(){// 指定小数最小宽度printf("%12f\n",123.45);// 包含小数整体和小数点,最小12位,不足补零,默认右对齐//限制小数点后位数(会四舍五入)printf("%.2f\n",123.4567);//最小宽度和限制小数点位数结合printf("%10.2f\n",123.4567);//通过参数控制printf("%*.*f\n",10,2,123.4567);// 10 传给第一个*,2 传给第二个*return0;}
5.1.4 输出部分字符串:
  • %.[m]s:指定输出长度
#include<stdio.h>// 标准输入输出的头文件intmain(){//输出部分字符串printf("%.5s\n","hello world");// 输出前五个字符printf("%[hello]","hewordllo");//在方括号中指定⼀组匹配的字符,遇到不在集合之中的字符,匹配将会停止return0;}

5.2. scanf

5.2.1 基本用法

接受用户输入的数据传递给变量。原型定义在头文件stdio.h。第一个参数是占位符,用于接受什么类型的数据,告诉编译器如何解读用户的输入。其余参数则是存放用户输入的变量。有多少个占位符,就有多少个变量。

scanf()处理数值占位符时,会自动过滤空白字符,包括空格、制表符、换行符等。所以,用户输入的数据之间,有⼀个或多个空格不影响scanf()解读数据。另外,用户使用回车键,将输入分成几行,也不影响解读。

经过测试,如果scanf函数中包含了除占位符之外的字符,scanf会理解为这是两个数据之间的分割符,同时用户也要在键盘输入这个字符。在这种情况下,第一个数据只能在前边可以有多个空格,后边必须紧接着就是分隔符,不能有空格,也不能进行回车,否则二个数据将不会被读取。

注意:

变量前面必须加上&运算符,因为scanf传递的不是值,而是地址,即将变量的地址指向用户输入的值。另外 scanf 遇到输入类型不匹配时并不会自动丢弃,而是将错误的输入留在输入缓冲区中。

测试:

#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>// 标准输入输出的头文件/* 在运行 scanf()函数会报错此函数可能不安全,要么替换为 scan_s()(vs自己提供的函数) 要么在源文件中加入 #define _CRT_SECURE_NO_WARNINGS 当我们在源文件中加入上述的 #define _CRT_SECURE_NO_WARNINGS 时,新建源文件使用 scanf 时还是需要加入。 在vs中创建 .c/.cpp 文件的时候,其实是拷贝了一个叫 newc++file.cpp的文件。 所以我们可以在 newc++file.cpp 文件中添加。 */intmain(){intage=0;intyear=0;printf("请输入你的年龄和出生年份:");//scanf 传递的不是值,而是地址,即将变量 age 和 year 的地址分别指向用户输入的值//%d 接受输入数据的类型,& 取变量的地址值scanf("%d/%d",&age,&year);//注意,此时第一个 %d 后有一个 / 说明键盘在两个整数之间也要输入 ///例如输入: 18/ 23// 第一个数据前面空格没有影响,但必须紧接着分隔符printf("您的年龄是:%d\n",age);printf("您的出生年份是:%d\n",year);return0;}

scanf处理用户输入的原理:

用户的输入先放入缓存,等到按下回车键后,按照占位符对缓存进行解读。 解读用户输⼊时,会从上⼀次解读遗留的第⼀个字符开始,直到读完缓存,或者遇到第⼀个不符合条件的字符为止。

#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>// 标准输入输出的头文件intmain(){printf("%s","请输入 x3 和 y3 的值:");intx3=0;floaty3=0.0f;// ⽤⼾输⼊" -13.45e12# 0"scanf("%d%f",&x3,&y3);printf("%d\n",x3);// 此时 x3 接受的是整数类型,所以 -13.45e12#,只能读取到 -13,停到小数点这里printf("%f\n",y3);// 从上次解读遗留的第一个字符开始,读取到 .45e12(0.45 * 10 的12 次方)//因为 # 不属于小数,所以会停在这里,同时浮点数在内存中有可能无法精确保存导致结果有误差return0;}
5.2.2 返回值

scanf()的返回值是⼀个整数,表示从左到右成功读取的变量个数。 如果没有读取任何项,或者匹配失败,则返回 0 。 如果在成功读取任何数据之前,发生了读取错误或者遇到读取到文件结尾,则返回常量EOF(-1)。

#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>// 标准输入输出的头文件intmain(){inta4=0;intb4=0;floatf4=0.0f;intr4=scanf("%d %d %f",&a4,&b4,&f4);printf("a=%d b=%d f=%f\n",a4,b4,f4);printf("r = %d\n",r4);return0;}
5.2.3 占位符

scanf占位符与printf基本一致。

scanf中除了%c以外,都会自动忽略起首的空白字符,%c不忽略。如果要想忽略起首的空白字符,需要在%c前加上一个空格即%c

注意:

scanf中的占位符%s:从当前第⼀个非空白字符开始读起,直到遇到空白字符(即空格、换行符、制表符等)为止。 因此%s不会包含空白字符,所以无法用来读取多个单词,除非多个%s⼀起使用。或者使用%[^\n]s此时也不忽略前面的空格。同时scanf遇到%s占位符时,会在字符串变量末尾存储⼀个空字符\0

另外scanf将字符串读入字符数组时,不会检测字符串是否超过了数组长度。所以在储存字符串时,很可能会超出数组的边界,为了防止这种情况,使用%s时,应指定读入字符串的最长长度,即写成%[m]s,m = 字符数组长度 -1,因为还有一个\0

#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>// 标准输入输出的头文件intmain(){//在 scanf 中,%c 不忽略前面的空格,如果忽略要在前面加上空格chara5=0;scanf(" %c",&a5);printf("%c----------\n",a5);// scanf 接受字符串// 其底层原理无法接受多个单词,假设单词之间有空白字符(空格,换行,制表符)识别到后便会自动加上 \0,除非多个 %s// 字符串:字符数组chararray1[11];chararray2[11];//在读取字符串不会检测数组的长度,所以需要指定长度确保安全scanf("%10s",array1);//数组不需要取地址,因为数组名就是地址,格式 %[m]s ,指定长度为10,剩下的一个留给\0scanf("%[^\n]s",array2);//此写法可以读取多个单词,遇到 \n 才会停止,但此时便不忽略前面的空格//scanf("%10[^\n]s", array2);//读取多个单词的同时,限制读取数组的长度printf("%s\n",array1);printf("%s\n",array2);return0;}
5.2.4 赋值忽略符

为了避免解析失败这种情况,可以使用赋值忽略符,把*加在任何占位符的百分号后⾯,便可忽略掉此占位符。该占位符不会返回值,解析后将被丢弃,同时 scanf 的返回值不包括被忽略的值。

#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>// 标准输入输出的头文件intmain(){// scanf 赋值忽略符intyear=0;intmonth=0;intday=0;printf("请输入年月日:");//scanf("%d%d%d",&year,&month,&day);// 此写法只能输入 年 月 日,经过实验 年-月-日 也可以,系统会当作负数处理不建议,年/月/日 则只会接受到年的数据//scanf("%d/%d/%d",& year, &month, &day)//此写法则只能读取 年/月/日 ,否则年后面的数据会解析失败,等同scanf("%d%c%d%c%d",&year,&ch1,&month,&ch2,&day);//为了避免解析失败这种情况,可以使用赋值忽略符,把 * 加在任何占位符的百分号后⾯,便可忽略掉此占位符// 该占位符就不会返回值,解析后将被丢弃。同时 scanf 的返回值即不被忽略值的数量intnum=scanf("%d%*c%d%*c%d",&year,&month,&day);//此写法可以不管数字中间输入什么单个字符都可以被接受printf("%d %d %d \n",year,month,day);printf("%d",num);return0;}
5.2.5 printf 和 scanf 差异
  • printf函数中%f可以表示小数floatdoubale,但是在scanf函数中,double必须使用%lf
  • printf函数中,%[m]s,其中 m 是一个整数,代表最小宽度,而在scanf函数中表示读取字符串的最大长度

声明

本文是在鹏哥 C 语言集训营学习过程中所记录的学习笔记,梳理了核心知识点,同时也记录了本人实操验证的代码案例,供后续学习复盘使用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/13 2:11:04

【CMake】在CMake项目中,Vcpkg、Conan或Spack用于C++依赖

#【CMake】在CMake项目中&#xff0c;Vcpkg、Conan或Spack用于C依赖 我最近用过一点 Vcpkg&#xff0c;也在更好地了解它。我也看过 Conan&#xff0c;但最近没怎么深入研究 Spack。我从开发者的角度来看&#xff0c;想改进第三方依赖的处理。这并不是要穷尽一切&#xff0c;而…

作者头像 李华
网站建设 2026/4/14 23:40:16

云手机 互联网 云端科技

云手机是云端科技在互联网环境下的具体应用&#xff0c;依托互联网与云端服务器相连&#xff0c;借助云端科技实现相关功能&#xff0c;三者紧密相关。互联网是连接用户与云手机的桥梁&#xff0c;用户通过互联网向云端服务器发送操作指令&#xff0c;如打开应用、播放视频等&a…

作者头像 李华
网站建设 2026/4/12 18:59:54

从待机功耗到峰值调度:智能Agent能源管理全流程详解

第一章&#xff1a;智能Agent能源管理的演进与挑战随着分布式计算和边缘智能的快速发展&#xff0c;智能Agent在能源管理系统中的角色日益关键。从早期基于规则的控制逻辑&#xff0c;到如今融合强化学习与联邦学习的自主决策系统&#xff0c;智能Agent已能动态响应电网负载、用…

作者头像 李华
网站建设 2026/4/7 23:08:03

Newtonsoft.Json 与 System.Text.Json 多态反序列化的安全性差异解析

多态反序列化是处理继承结构对象序列化的常见需求&#xff0c;但不同 JSON 序列化库的实现机制差异会带来显著的安全风险。微软 CA2326 规则明确警示&#xff1a;避免使用非安全的 JsonSerializerSettings 配置&#xff08;如 Newtonsoft.Json 的 TypeNameHandling 非 None 值&…

作者头像 李华
网站建设 2026/4/15 14:45:17

基于Spring Boot的大数据商品推荐系统

是一个强大且智能的推荐工具&#xff0c;它充分利用大数据技术&#xff0c;广泛收集和整合海量的商品数据以及用户行为数据&#xff0c;旨在为用户提供个性化、精准的商品推荐服务。以下是对该系统的详细介绍&#xff1a; 一、系统架构 该系统采用前后端分离的架构模式。后端使…

作者头像 李华
网站建设 2026/4/15 12:13:54

基于Spring Boot的新农村自建房改造管理系统

基于Spring Boot的新农村自建房改造管理系统是一款专为新农村建设中自建房改造项目设计的高效管理工具。以下是对该系统的详细介绍&#xff1a; 一、系统背景与意义 随着国家对新农村建设的大力推进&#xff0c;农村自建房改造成为改善农村居住环境、提升农民生活质量的重要举措…

作者头像 李华