C语言笔记(一些疑问和解释说明)

核心语法1

  • 变量

变量是用来存储数据的,类似于放数据的盒子,而不是数据本身。

  • 四种数据类型变量定义

1.short  短整型     在Windows中占2个字节,取值范围(-32768~32767)。

用sizeof测量每一种数据类型占多少字节

sizeof使用规则:sizeof(变量名/数据类型),在输出打印时占位符用:"%zu",这个占位符是用来表示”sizeof“的测量结果。

#include <stdio.h>

int main()
{
//定义一个变量
short a = 10;
//打印
printf("%d\n",a);
//利用sizeof测量每一种数据类型占多少字节
printf("%zu\n",sizeof(short));//数据类型

printf("%zu\n",sizeof(a));//变量名


return 0;
}

 如果我赋值超过数据类型取值范围会怎样呢,以short为例

#include <stdio.h>

int main()
{
//定义一个变量
short a = 32768;
//打印
printf("%d\n",a);
//利用sizeof测量每一种数据类型占多少字节
printf("%zu\n",sizeof(short));//数据类型

printf("%zu\n",sizeof(a));//变量名


return 0;
}

运行结果:可以看到它回到了最开始的值(-32768~32767),好像是一个人绕地球跑了一圈回到起点。

2.int  整型    在Windows中占4个字节,取值范围(-2147483648~217673647)。

#include <stdio.h>

int main()
{
//定义一个变量
int a = 100;
//打印
printf("%d\n",a);

printf("%zu\n",sizeof(int));//数据类型

printf("%zu\n",sizeof(a));//变量名

return 0;
}

3.long 长整型   在Windows中占4个字节,取值范围(-2147483648~217673647)。

#include <stdio.h>

int main()
{
//定义一个变量
long a = 1000L;
//打印
printf("%lf\n",a);

printf("%zu\n",sizeof(long));//数据类型

printf("%zu\n",sizeof(a));//变量名

return 0;
}

在这里需要注意的是long型在进行定义时需要在数据后面加后缀“L”,并且在输出打印时占位符要换成  "%lf"

4.long long (C99)超长整型  在Windows中占8个字节,取值范围有19位,一般不常使用。

#include <stdio.h>

int main()
{
//定义一个变量
long long a = 10000LL;
//打印
printf("%llf\n",a);

printf("%zu\n",sizeof(long long));//数据类型

printf("%zu\n",sizeof(a));//变量名


return 0;
}

 在这里需要注意的是long型在进行定义时需要在数据后面加后缀“LL”(这里用大写是为了和数字‘1’区别开),并且在输出打印时占位符要换成  "%llf"

  • 补充

1.定义变量的完整形态(小括号里面的int可以省略不写)

在c语言中省略特定的数据类型中的int系统是允许的:

短整型:short (int);整型:int;长整型:long (int);超长整型:long long (int)

这里可以把 int 看成一个基础数据类型,其他三个都是由 int 衍生出来的。比如:

#include <stdio.h>

int main()
{
//定义一个变量
long long int a = 10000LL;
//打印
printf("%llf\n",a);

printf("%zu\n",sizeof(long long));//数据类型

printf("%zu\n",sizeof(a));//变量名


return 0;
}
2.有符号整数与无符号整数的定义

有符号整数(singed)——正、负数(占位符与数据类型保持一致且”singed“可省略)

无符号整数(unsinged)——只有正数 (占位符是%u)

有符号的取值范围和原来保持不变,但是无符号的取值范围是将左边的负数加到右边,比如;短整型有符号取值范围是(-32768~32767),无符号取值范围是(0~65535),那么其他的数据类型无符号取值是:整型/长整型:(0~4294967295)

#include <stdio.h>

int main ()
{
signed int a = -100;

unsigned int b = 100;

printf("%d\n",a);

printf("%u\n",b);



return 0;
}

运行结果

如果将unsigned后面的赋值改为负数会怎么样呢?

#include <stdio.h>

int main ()
{
signed int a = -100;

unsigned int b = -100;

printf("%d\n",a);

printf("%u\n",b);



return 0;
}

运行结果:可以看到并未没有得到我们想要的数,虽然不会报错。 但在这里4294967196+99=4294967295,为什么不是加100呢?

 一些解释:

这里的关键在于理解有符号整数和无符号整数之间的转换以及它们是如何在二进制层面上表示的。

在大多数现代计算机系统中,整数通常使用补码表示法来表示。对于有符号整数,最高位(最左边的位)是符号位,0 表示正数,1 表示负数。对于无符号整数,所有位都用于表示数值的大小,没有专门的符号位。

当您将一个负数(如 -100)赋值给一个无符号整数类型(如 unsigned int)时,该负数会被转换为无符号整数的等效值。这个转换过程实际上是在二进制层面上进行的,它涉及到将负数的补码表示形式视为一个无符号数的二进制表示形式。

在 32 位系统中,-100 的补码表示形式是一个 32 位的二进制数,其中最高位(符号位)是 1,其余位表示该数的绝对值(100)的补码。当这个二进制数被视为一个无符号整数时,它会被解释为一个非常大的正数,因为所有位都被用于表示数值的大小。

现在,让我们来看看为什么 4294967196 + 99 = 4294967295 而不是加 100

在 32 位无符号整数中,4294967196 的二进制表示形式与 -100 的补码表示形式相同(只是解释方式不同)。当您在这个数上加 99 时,您实际上是在二进制层面上进行加法运算。由于这个数已经非常接近 32 位无符号整数的最大值(2^32 - 1,即 4294967295),所以加上 99 就会导致溢出到最大值的前一个数再加上 99 等于最大值。

换句话说,4294967196(二进制上等同于 -100 的补码)加上 99 正好等于 42949672952^32 - 1),因为 4294967196 距离 4294967295 只有 99 的差距。

如果您尝试加上 100 而不是 99,您将会得到一个溢出的结果,这取决于您的编译器和系统的行为。在大多数情况下,对于无符号整数运算,溢出是静默发生的,并且结果会被截断到该类型的有效位数范围内。因此,如果您尝试计算 4294967196 + 100,您可能会得到一个看起来不相关的结果,因为它已经超出了 32 位无符号整数的表示范围。然而,在这个特定的例子中,由于 4294967196 加 100 实际上会超出最大值并回绕到 0(对于模 2^32 运算),但在这个上下文中我们关注的是加到 99 时的行为。

小数的数据类型(float、double)

float,在32/64位操作系统中的取值范围是 1.175*10^(-38) ~ 3.402*10^(38),在Windows中占4个字节

double,在32/64位操作系统中的取值范围是 2.225*10^(-308) ~ 1.797*10^(308),在Windows中占8个字节

不同的数据类型所表示的范围和内存大小都不一样,这是由编译器来决定的,可以用sizeof来测量不同数据类型的长度大小。

小数的取值范围比整数大:double > float > long long > long > int > short

小数类型不能和 unsigned 组合,unsigned 只能和整数类型组合

定义 float、double、long double、数据类型的变量,并用sizeof进行测量占用内存。

float:单精度小数(精确到后6位)Win:4 字节,最大长度38位,定义时加后缀 " %F/f ",占位符位 " %f " 。

#include <stdio.h>

int main ()
{
float a = 1.68F;

printf ("%f\n",a);

printf ("%0.2f\n",a);//显示小数点后两位只需写成" %0.2f\n "即可

printf ("%zu\n",sizeof(float));//测量数据类型

printf ("%zu\n",sizeof(a));//测量变量名

return 0;
}

运行结果:

double:双精度小数(精确到后15位)Win:8 字节,最大长度308位,定义时无后缀,占位符为:" %lf "。

一般为说明数据类型的小数,默认使用double

#include <stdio.h>

int main ()
{
double a = 3.14;

printf ("%lf\n",a);

printf ("%0.2lf\n",a);//显示小数点后两位只需写成" %0.2lf\n "即可

printf ("%zu\n",sizeof(double));

printf ("%zu\n",sizeof(a));

return 0;
}

运行结果:

long double:单精度小数(精确到后18~19位)Win:8 字节,其它系统有12,16字节,定义时加后缀 "L" 它的占位符为 "%Lf "

#include <stdio.h>

int main ()
{
long double a = 3.1415926L;

printf ("%Lf\n",a);

printf ("%0.2Lf\n",a);//显示小数点后两位只需写成" %0.2Lf\n "即可

printf ("%zu\n",sizeof(long double));

printf ("%zu\n",sizeof(a));

return 0;
}

运行结果:

  • 字符的数据类型(char)

char:取值范围ASCII码表中的字母、数字、英文符号(不能有汉字和中文符号),占1个字节,占位符为 "%c" 。在定义char类型时要注意它字符的格式 'xxx' 。

#include <stdio.h>

int main ()
{
char A = 'a';

printf ("%C\n",A);

printf ("%zu\n",sizeof(char));

printf ("%zu\n",sizeof(A));


return 0;
}

运行结果为:

核心语法2

  • 标识符

标识符:代码中所有我们自己起的名字,比如变量名,函数名等。

命名规则:

硬性条件:

1.由数字、字母、下划线( _ )组成

2.不能以数字开头

3.不能是关键字

4.区分大小写 

软性建议:变量名用英文单词(见名知意),代码文件名全部小写,单词之间用下划线( _ )隔开,开头可以用数字。

  • 键盘录入 scanf

scanf是scanner format 缩写,是C语言提供的一个函数。、

作用:获取用户在键盘上输入的数据,并且赋值给变量

使用方式:scanf ("数据类型占位符",&变量名);它表示把输入的信息赋值给变量。“&”这个是固定格式,不能丢掉。

键盘录入的基本使用

#include <stdio.h>

int main ()
{
int a;//只定义不赋值

scanf ("%d",a);//键盘录入

printf ("%d\n",&a);//输出打印键盘录入后的值


return 0;
}

运行结果:这里我输入123,给我的结果也是123

这种代码看上去比较糊涂,可以在录入之前加上一些解释文字与后面打印的字作区分,这里我想键盘录入女朋友的年龄。

#include <stdio.h>

int main ()
{
int a;//只定义不赋值

printf ("请输入年龄:");

scanf ("%d",&a);//键盘录入

printf ("我女朋友的年龄是%d岁",a);//输出打印键盘录入后的值


return 0;
}

运行结果:

 字符串变量的定义方式(char):

数据类型  变量名 [内存占用大小] = "字符串",字符串占位符:"%s"; 比如:

char str [内存占用大小] = ”aaa“ ;

如果只是:“char str” 这样 ,那就意味着只定义了一个字符,而字符串是由多个字符拼在一起的,所以要用上述定义方法定义。

内存占用大小的计算方式:

在英文中:一个字母、符号、数字占一个字节

在中文中:在vs/vc中,默认情况下占两个字节

结束标记:一个字节

在上面这段不完整的代码中结束标记就在aaa后面即蓝色划线处,它也占一个字节,三个a共三个字节再加结束标记共四个字节,所以内存占用大小写4。通常情况下,为了方便总是将这个内存占用大小的这个数取的比较大,这样做免去了计算字节的过程。

举例:这里我要用scanf输出“我可爱女朋友的名字是:”

#include <stdio.h>

int main ()
{

char name [100];//因为是键盘录入,所以这里是只定义变量但是不赋值

printf ("请输入姓名:");//给一个输入提示

scanf ("%s",&name);//键盘录入女朋友的名字

printf ("我可爱女朋友的名字是:%s\n",name);//输出打印


return 0;
}

运行结果:小谷谷。在上述代码中内存占用大小就给了100,实际上小谷谷占6个字节加结束标记总共占用7个字节,所以内存给100个字节完全够用,这样也是计算变得简单。

错误代码的一些理解:

#include <stdio.h>

int main ()
{

char name [100];

printf("请输入姓名:");

scanf("%s\n",&name);//错误使用换行符

printf("我可爱女朋友的名字是:%s\n",name);

return 0;
}

 运行结果:可以看到在输入“小谷谷”之后按回车并没有直接出现输出结果,而是输入其他符号之后在按回车才出现我们想要的结果,这是为什么呢?

这里涉及到对scanf的一些理解:

关键点在于理解 scanf 如何处理输入,特别是当涉及到字符串和空白字符时:

  1. 自动跳过前导空白scanf 在开始读取输入之前会自动跳过任何前导的空白字符。这意味着,如果用户在输入字符串之前按下了几个空格或制表符,这些字符将被忽略,scanf 将从第一个非空白字符开始读取。

  2. 遇到空白停止:当 scanf 使用 %s 格式说明符读取字符串时,它会继续读取字符,直到遇到第一个空白字符。这意味着字符串中的空格、制表符或换行符不会被存储在目标数组中。相反,scanf 会在遇到这些字符时停止读取,并在目标数组的相应位置添加一个空字符('\0'),以标记字符串的结束。

  3. 换行符的处理:当用户按下回车键时,实际上是在输入流中插入了一个换行符('\n')。然而,由于 scanf 在遇到空白字符时停止读取,这个换行符不会被存储在使用 %s 读取的字符串中。但是,换行符仍然留在输入缓冲区中,这可能会影响后续的输入操作。

  4. 格式字符串中的 \n:在 scanf 的格式字符串中,通常不应该放置 \n 来尝试跳过换行符。这是因为 scanf 在读取 %s 时已经会自动在遇到空白字符(包括换行符)时停止。在格式字符串中放置 \n 实际上是不必要的,并且可能会导致未定义的行为或使 scanf 等待另一个非空白字符的输入(尽管在某些实现中,\n 在格式字符串中可能只是被简单地忽略)

  5. 安全的输入:由于 scanf 使用 %s 读取字符串时不会检查目标数组的大小,因此存在缓冲区溢出的风险。如果用户输入的字符串超过了目标数组的大小,将会导致未定义的行为,可能是内存损坏或程序崩溃。为了避免这种情况,可以使用更安全的输入函数,如 fgets,它允许指定读取的最大字符数,并在遇到换行符或达到最大字符数时停止读取。

综上所述,当使用 scanf 和 %s 读取字符串时,重要的是要理解它如何处理输入,特别是空白字符和换行符,以及如何避免潜在的缓冲区溢出问题。

  • 键盘录入多个数据

scanf("占位符 占位符 占位符",&数据1,&数据2,&数据3);,注意这里占位符的数据类型和后面的数据是一一对应关系(个数一致,类型一致),需要注意的是占位符与占位符之间不管使用空格或者逗号隔开,在输入时数据与数据之间也要与占位符保持一致,这里建议占位符之间的间隔符号用空格。比如:键盘录入三个变量并且求和,结果保留两位小数

#include <stdio.h>

int main ()
{
int a;

int b;

double c;

printf("请分别输入两个整数和一个小数:\n");

scanf("%d %d %lf",&a,&b,&c);//键盘录入

printf("这三个数的和为:%0.2lf",a+b+c);//输出打印保留两位

return 0;
}

运行结果:2,3,3.14(在这里占位符与占位符之间是以空格隔开,在输入时数据与数据之间也要用空格隔开)

小练习1:键盘输出你的姓名、年龄、身高,并打印出来

打印效果如下:

我的姓名为:xxx,年龄为:xx岁,身高为:xxx米。

#include <stdio.h>

int main ()
{
char name [100];

int age;

double length;

printf ("请依次输入你的姓名、年龄、身高:\n");

scanf ("%s %d %lf",&name,&age,&length);//注意%0.2lf在scanf中使用时无效的,并且可能会导致未定义的报错或警告

printf ("你的姓名为:%s,你的年龄为:%d,你的身高为:%0.2lf米",name,age,length);


return 0;
}

 运行结果:

 小练习2:键盘录入三个小数,分别表示长方体的长、宽、高,分别求A面、B面、C面的面积以及长方体的体积,结果保留两位小数。

#include <stdio.h>

int main ()
{//定义变量
double chang;

double kuan;

double gao;

printf("请分别输入三个小数表示长、宽、高:\n");

scanf("%lf %lf %lf",&chang,&kuan,&gao);//键盘录入

double areaA = chang*kuan;//面积赋值

double areaB = kuan*gao;

double areaC = chang*gao;

printf("A面的面积为:%0.2lf\n",areaA);//输出打印

printf("B面的面积为:%0.2lf\n",areaB);

printf("C面的面积为:%0.2lf\n",areaC);

double bluk = chang*kuan*gao;//体积赋值

printf("这个长方体的体积为:%0.2lf",bluk);//输出打印

return 0;
}

 运行结果:

  • 运算符

算数运算符:+  -  *  /  %(取余)

自增自减运算符:++   - -

赋值运算符:=  +=  -=  *=  /=  %=

关系运算符:==  !=  >  >=  <  <=  (大于小于的判断)

逻辑运算符:!  &&  | |  (“或者、而且”的一些关系)

三元运算符:a > b ? a : b 

  • 算数运算符:+  -  *  /  %

#include <stdio.h>

int main ()
{
//直接计算
printf("%0.2lf",3.1+3.2);

printf("%0.2lf",3.1-3.2);

printf("%0.2lf",3.1*3.2);

printf("%0.2lf",3.1/3.2);


//变量计算

double a = 3.14;

int b = 5;

printf("%0.2lf",a+b); 

printf("%0.2lf",a-b); 

printf("%0.2lf",a*b); 

printf("%0.2lf",a/b); 


 
return 0;
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值