编译器的工作流程

大端和小端

使用补码的好处

1、转义字符

2、scanf()

scanf()函数解析:由scanf说起之1:scanf函数和回车、空格 及其返回值

视频见:小甲鱼,VIP资料\【视频教程】C++快速入门\第二讲和第三讲

要求:编写一个程序,要求用户输入一串整数和任意数目的空格,这些整数必须位于同一行中,但允许出现在该行中的任何位置。当用户按下键盘上的“Enter”键时,数据输入结束。程序自动对所有的整数进行求和并打印出结果。

示例代码:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>void main()
{int i;int sum = 0;char ch;printf("请输入一串整数和任意数目的空格: ");while (scanf("%d", &i) == 1){sum += i;while ((ch = getchar()) == ' ') //屏蔽空格;ch = getchar();if (ch == '\n'){break;}ungetc(ch, stdin); //将变量ch存放的字符退回给stdin输入流}printf("结果是: %d", sum);printf("\n");system("pause");
}

比较好的习题,用到了scanf函数:https://fishc.com.cn/forum.php?mod=viewthread&tid=68999&extra=page%3D1%26filter%3Dtypeid%26typeid%3D570

3、printf()

https://fishc.com.cn/thread-66471-1-1.html

4、pow()

函数原型:
#include <math.h>
...
double pow(double x, double y);

https://fishc.com.cn/thread-67234-1-1.html

扩展:浮点数和科学记数:

科学记数法

浮点数:表示更大范围的小数(#)

5、进制转换&补码

进制转换

示例:二进制(补码)-> 十进制

对于有符号数(补码)来说:
如果符号位为 0,表示该数为正数,转换跟无符号数没什么两样。
如果符号位为 1,表示该数为负数,此时符号位的位权不变,但该位的权值应该乘以 -1 得到。
举个栗子,将有符号数 0011 1100 转换成十进制数,与无符号数的做法是一样的:

 0 * 2^7 + 0 * 2^6 + 1 * 2^5 + 1 * 2^4 + 1 * 2^3 + 1 * 2^2 + 0 * 2^1 + 0 * 2^0= 32 + 16 + 8 + 4= 60

然后如果符号位为 1,表示这是一个负数,比如 1011 1100,那么符号位的权值就应该乘以 -1 得到:

 -1 * 2^7 + 0 * 2^6 + 1 * 2^5 + 1 * 2^4 + 1 * 2^3 + 1 * 2^2 + 0 * 2^1 + 0 * 2^0= -128 + 32 + 16 + 8 + 4= -68

在举个极端点的例子,比如 1000 0000:

  -1 * 2^7 + 0 * 2^6 + 0 * 2^5 + 0 * 2^4 + 0 * 2^3 + 0 * 2^2 + 0 * 2^1 + 0 * 2^0= -128

补码:

正数的补码是该数的二进制形式。
负数的补码需要通过以下几步获得:
  • 先取得该数的绝对值的二进制形式
  • 再将第1步的值按位取反
  • 最后将第2步的值加1

基本数据类型的取值范围:

数据类型

字节数

取值范围

char

1

-128 ~ 127

unsigned char

1

0 ~ 255

short

2

-32768 ~ 32767

unsigned short

2

0 ~ 65535

int

4

-2147483648 ~ 2147483647

unsigned int

4

0 ~ 4294967295

long

4

-2147483648 ~ 2147483647

unsigned long

4

0 ~ 4294967295

long long

8

-9223372036854775808 ~ 9223372036854775807

unsigned long long

8

0 ~ 18446744073709551615

数据类型

字节数

取值范围(绝对值)

float

4

1.17549 * 10-38 ~ 3.40282 * 1038

double

8

2.22507 * 10-308 ~ 1.79769 * 10308

long double

12

2.22507 * 10-308 ~ 1.79769 * 10308

关于 定点数、浮点数 解释:

https://fishc.com.cn/forum.php?mod=viewthread&tid=67265&extra=page%3D1%26filter%3Dtypeid%26typeid%3D584

6、运算符的优先级和结合性

优先级

运算符

含义

使用形式

结合性

说明

1

[ ]

数组下标

数组名[整型表达式]

左到右

 

( )

圆括号

(表达式)

 

.

成员选择(对象)

对象.成员名

 

->

成员选择(指针)

对象指针->成员名

 

++

自增运算符

变量名++

单目运算符

--

自减运算符

变量名--

单目运算符

2

-

负号运算符

-表达式

右到左

单目运算符

(类型)

强制类型转换

(数据类型)表达式

单目运算符

++

自增运算符

++变量名

单目运算符

--

自减运算符

--变量名

单目运算符

*

取值运算符

*指针表达式

单目运算符

&

取地址运算符

&左值表达式

单目运算符

!

逻辑非运算符

!表达式

单目运算符

~

按位取反运算符

~表达式

单目运算符

sizeof

长度运算符

sizeof 表达式

sizeof(类型)

单目运算符

3

/

表达式 / 表达式

左到右

双目运算符

*

表达式 * 表达式

双目运算符

%

余数(取模)

整型表达式 % 整型表达式

双目运算符

4

+

表达式 + 表达式

左到右

双目运算符

-

表达式 - 表达式

双目运算符

5

<<

左移

表达式 << 表达式

左到右

双目运算符

>>

右移

表达式 >> 表达式

双目运算符

6

>

大于

表达式 > 表达式

左到右

双目运算符

>=

大于等于

表达式 >= 表达式

双目运算符

<

小于

表达式 < 表达式

双目运算符

<=

小于等于

表达式 <= 表达式

双目运算符

7

==

等于

表达式 == 表达式

左到右

双目运算符

!=

不等于

表达式 != 表达式

双目运算符

8

&

按位与

整型表达式 & 整型表达式

左到右

双目运算符

9

^

按位异或

整型表达式 ^ 整型表达式

左到右

双目运算符

10

|

按位或

整型表达式 | 整型表达式

左到右

双目运算符

11

&&

逻辑与

表达式 && 表达式

左到右

双目运算符

12

||

逻辑或

表达式 || 表达式

左到右

双目运算符

13

?:

条件运算符

表达式1? 表达式2: 表达式3

右到左

三目运算符

14

=

赋值运算符

变量 = 表达式

右到左

双目运算符

/=

除后赋值

变量 /= 表达式

双目运算符

*=

乘后赋值

变量 *= 表达式

双目运算符

%=

取模后赋值

变量 %= 表达式

双目运算符

+=

加后赋值

变量 += 表达式

双目运算符

-=

减后赋值

变量 -= 表达式

双目运算符

<<=

左移后赋值

变量 <<= 表达式

双目运算符

>>=

右移后赋值

变量 >>= 表达式

双目运算符

&=

按位与后赋值

变量 &= 表达式

双目运算符

^=

按位异或后赋值

变量 ^= 表达式

双目运算符

|=

按位或后赋值

变量 |= 表达式

双目运算符

15

,

逗号运算符

表达式1,表达式2,表达式3,…

左到右

 

注1:优先级相同的运算符,运算次序由结合性决定。

  • 比如 3 * 4 / 5 % 6,其中乘号、除号和求余运算符在同一优先级,结合性是“左到右”,因此先计算 3 * 4,再计算 / 5,最后计算 % 6
  • 再如 -i++,符号和自增运算符在同一优先级,结合性是“右到左”,因此 -i++ 相当于 -(i++)

注2:* 目运算符是指操作数的个数,比如单目运算符只有一个操作数,双目运算符有两个操作数,而三目运算符则有三个操作数。

注3:通过使用小括号可以提升表达式的优先级至最高。

  • 比如 (1 + 2) * (3 - 4),其中加号、减号运算符的优先级本身是要低于乘号运算符的,但由于使用了小括号,且结合性均是“左到右”,所以先计算 (1 + 2),再计算 (3 - 4),最后才将前两者的结果相乘。

运算符

名称

例子

结果

+

加法运算符(双目)

5 + 3

8

-

减法运算符(双目)

5 - 3

2

*

乘法运算符(双目)

5 * 3

15

/

除法运算符(双目)

5 / 3

1

5.0 / 3.0

1.666667

%

求余运算符(双目)

5 % 3

2

5.0 % 3.0

出错

+

正号运算符(单目)

+5

5

-

负号运算符(单目)

-5

-5

算术运算符知识点:https://fishc.com.cn/forum.php?mod=viewthread&tid=67733&extra=page%3D1%26filter%3Dtypeid%26typeid%3D584

求余运算符两边要求均为整型 ;

关系运算符,得到的为逻辑值:真、假;

逻辑表达式:!、&&、||;逻辑非、逻辑与、逻辑或;

逻辑运算符注意: 短路求值原则;

7、switch case

case 后边应该只能跟整型常量或常量表达式,不能是浮点型常量。

任意两个 case 跟随的整型常量值不能相同(否则编译系统无法判断你要往哪儿跳转)。

[知识点备忘] S1E12:switch语句和分支嵌套

[课后作业] S1E12:switch语句和分支嵌套 | 课后测试题及答案

8、break 和 continue 语句

[知识点备忘] S1E15:break语句和continue语句

9、左值和右值

赋值运算符的左边必须是一个 lvalue,变量名就是 lvalue,但常数就不是了,所以你把 5 写在赋值号的左边就会出错:

5 = a;

什么是左值(lvalue)和右值(rvalue)?

知识点总结:https://fishc.com.cn/forum.php?mod=viewthread&tid=69845&extra=page%3D1%26filter%3Dtypeid%26typeid%3D584

10、第一阶段考核:

[阶段考核] 第一阶段考核(考核S1E1~S1E16知识点)

答案:进入答案的密码为:233168

[阶段考核] 第一阶段考核答案及解析(密码:第 0 题的答案)

11、数组课后测试题中最后两个比较经典

https://fishc.com.cn/forum.php?mod=viewthread&tid=70264&extra=page%3D1%26filter%3Dtypeid%26typeid%3D570

关于闰年判断;

1. 写一个生命计算器,要求用户输入生日,显示他在这个世界上活了多少天?c

12、字符串处理函数

定义一个字符数组的方式:

#include <stdio.h>int main()
{char str[10] = { 'F','i','s','h','C','\0'};char str1[] = {"FishC"};char str2[] = "Fishc";return 0;
}

str、str1、str2:

输入一个字符串的方式1:

        while ((str2[i++] = getchar()) != '\n');

输入一个字符串的方式2:

gets(str);

不使用 scanf 函数接收字符串是因为 scanf 遇到空格会终止;

(1)函数strlen、关键字:sizeof

strlen 函数文档

请问下边程序会打印什么内容?

#include <stdio.h>
#include <string.h>int main()
{char str[] = "I love FishC.com!";str[7] = '\0';printf("sizeof str = %d\n", sizeof(str));printf("strlen str = %d\n", strlen(str));return 0;
}

答:sizeof 运算符是取得字符串的尺寸,即该字符串所处存储空间的大小。.Wn8E

代码开头的 char str[] = "I love FishC.com!" 决定了该字符数组的尺寸。而字符串的长度则是由第一个遇到的结束符('\0')所定义的。只要编译器读取到结束符('\0'),它不管你字符数组后边是否有其它内容,都会认为字符串已经结束。

即最后结果为:

sizeof(str)=18; strlen(str)=7

(2)拷贝字符串 strcpy() strcpy 函数文档

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>int main()
{char str1[] = "Original String";char str2[] = "New String";char str3[100];strcpy(str1, str2);strcpy(str3, "Copy Successful");printf("str1: %s\n", str1);printf("str2: %s\n", str2);printf("str3: %s\n", str3);return 0;
}

拷贝字符串(受限),需要在目标数组中添加'\0'  strncpy 函数文档

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>int main()
{char str1[] = "To be or not to be";char str2[40];char str3[40];strncpy(str2, str1, sizeof(str2));strncpy(str3, str2, 5);str3[5] = '\0';printf("%s\n", str1);printf("%s\n", str2);printf("%s\n", str3);return 0;
}

请问下边代码将打印什么内容?

#include <stdio.h>
#include <string.h>int main()
{char str1[100] = "Original";char str2[10] = "New";strcpy(str1, str2);printf("%s\n", str1);return 0;
}

答:结果应该是只打印 New 这个字符串。K@$,GB
Wb#M)C*wA{DxJkl5d[-$
有些同学可能会觉得 str1 的长度比 str2 长,调用 strcpy 函数后会只覆盖前边的内容,后边保留……但事实上并不是这样,因为 strcpy 函数复制 str2 的时候,会将该字符串最后的 '\0' 也一并复制过去。

如下图所示:版权

(3)连接字符串 strcat();strcat 函数文档

1、函数自动在末尾添加'\0',

2、同时要保证目标数组足够大,容纳下待连接的字符数组;

连接字符串 strncat() 受限; strncat 函数文档

(4)比较字符串strcmp()  strcmp 函数文档

对两个字符数组的字符逐个比较ASCII大小

如果str1==str2,返回0;

如果str1<str2,返回-1;

如果str1>str2,返回1;

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>int main()
{char str1[10] = "FishC.com";char str2[20] = "fishC.com";if (!strcmp(str1, str2)){printf("两个字符串完全一致!\n");printf("%d\n", strcmp(str1, str2));}else{printf("两个字符串不同!\n");printf("%d\n", strcmp(str1, str2));}return 0;
}

比较字符串(受限) strncmp 函数文档

13、二维数组

定义一个变量,定义一个一维数组、定义一个二维数组;可以认为为:点、线、面

测试程序的小模版:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main() {#if 0#endifreturn 0;
}

获取二维数组的长度:

sizeof(a) / sizeof(a[0][0])

14、指针和指针变量

指针是一个地址/内存的地址;

指针变量是存放地址的变量;指针变量存在类型,其类型指的是存放地址指向的数据类型;(32位系统,指针变量是4个字节)

普通变量存放的是数据;

取地址运算符/&和取值运算符/*

直接访问通过变量名,间接访问通过指针

指针未初始化,即没有给指针赋值;

[知识点备忘] S1E21:指针

取址操作符(&)的作用对象应该是一个左值。

重要的内容要重复强调:C 语言的术语 lvalue 指用于识别或定位一个存储位置的标识符。(注意:左值同时还必须是可改变的)

什么是左值(lvalue)和右值(rvalue)?

[课后作业] S1E21:指针 | 课后测试题及答案

15、数组 和 指针

数组不是指针、指针不是数组;

数组名只是一个地址,而指针是一个左值;什么是左值(lvalue)和右值(rvalue)?

数组名其实是数组第一个元素的地址;(也即可以将数组名当作一个指针,用指针法对数组元素进行操作)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {char str[50];printf("请输入鱼C的域名:");scanf("%s",str);printf("鱼C工作室的域名为:%s\n",str);//输出数组str地址printf("数组str首地址:%p\n",str); //数组名即为数组第一个元素地址printf("数组str首地址:%p\n", &str[0]); //数组首个元素的地址,即为数组地址printf("数组str首地址:%p\n", &str);//str[50],中50个元素为整体/即数据类型为 char str[50];(&str+1)为平移50个地址printf("数组str首地址:%p\n",( &str+1)); return 0;
}

显示结果:

上面的代码:char str[50]; (&str+1)指向了哪里?答:指向了以50个char类型数组的下一个数组的开始;这个思想很重要!

对于一个数组中元素的操作:采用基本的数组法,按照数组规则运行;或者指针法,定义一个指向数组的指针,然后访问;

指针可以指向一个地址,也可以指向一个字符串;

char *p="I Love Beijing"

请问 str[20] 是否可以写成 20[str]?V

答:可以。Powered by bbs.fishc.com

因为在访问数组的元素的时候,数组名被解释为数组第一个元素的地址。JS2?FT

所以 str[20] == *(str + 20) == *(20 + str) == [20]strBJ]_,P

你们可能对 *target++ != '\0' 这一行代码有疑问,这里我给大家解释下。首先在“运算符的优先级和结合性”(http://bbs.fishc.com/thread-67664-1-1.html)可以查到自增运算符(++)的优先级比取值运算符(*)要高,所以 *target++ 相当于 *(target++),先执行自增运算符,再取值。但由于这是一个后缀的自增运算符,所以自增的效果要在下一条语句才会生效,因此这里取出来的依然是 target 地址自增前指向的数组元素的值。

基本要求:使用 fgets 函数接收用户输入的两个字符串到 str1 和 str2 中,将 str2 连接到 str1 后边,并打印出来。u

程序如下:

#include <stdio.h>#define MAX 1024int main()
{char str1[2 * MAX]; // 确保连接后不越界char str2[MAX];char *target1 = str1;char *target2 = str2;printf("请输入第一个字符串:");fgets(str1, MAX, stdin);printf("请输入第二格字符串:");fgets(str2, MAX, stdin);// 将指针指向 str1 的末尾处while (*target1++ != '\0');// 我们希望 str1 最后边的 '\0' 和 '\n' 都被覆盖掉target1 -= 2;// 连接字符串while ((*target1++ = *target2++) != '\0');printf("连接后的结果是:%s", str1);return 0;
}

重点关注下,这节课的课后习题:

[课后作业] S1E22:指针和数组 | 课后测试题及答案

16、指针数组  和 数组指针

看后面两个字;原理:根据优先级,[ ]优先级高于 *

指针数组:是一个数组,每个数组元素存在一个指针变量;int *p1[5];

[ ]、() 优先级相同,结合顺序为:从左向右;

数组指针:是一个指针,它指向一个数组; int (*p1)[5]

[知识点备忘] S1E23:指针数组和数组指针

比如定义一个数组:

int array[5]={1,2,3,4,5};

请问 array 和 &array 有区别吗?!IA|i8Xt
O!`cU4-,e.&ws[DdNGqlhTCBP7~H
答:有。版权属于:bbs.fishc.com
L=a{SYBZE3>dVMc;<p!N%Ciy7+P:
解析:虽然 array 和 &array 的值相同,但含义是不一样的。array 表示数组第一个元素的位置,而 &array 表示的是整个数组的位置(这里将数组看做一个整体)。

#include <stdio.h>int main()
{int array[2][3] = {{0, 1, 2}, {3, 4, 5}};int (*p)[3] = array;printf("**(p+1): %d\n", **(p+1));printf("**(array+1): %d\n", **(array+1));printf("array[1][0]: %d\n", array[1][0]);printf("*(*(p+1)+2): %d\n", *(*(p+1)+2));printf("*(*(array+1)+2): %d\n", *(*(array+1)+2));printf("array[1][2]: %d\n", array[1][2]);return 0;
}

运行结果:

17、指针 和 二维数组

如果要使用指针来指向二维数组,只能使用数组指针。重点!

[扩展阅读] 什么是语法糖(Syntactic sugar

在 C 语言里用 a[n] 表示 *(a+n),用 a[n][m] 表示 *(*(a+n)+m),这就是语法糖的应用,因为在内部,编译器会自动将 a[n] 转换为 *(a+n) 的形式实现。

*(array +1)  == array[1] //第二行首元素的地址

*(array+1)表示 数组第二行 ,第一个元素的地址

#include <stdio.h>int main()
{int array[2][3] = { {0, 1, 2}, {3, 4, 5} };printf("&array[0][0]=%p\n",&array[0][0]);printf("&array[0]=%p\n", &array[0]);printf("&array=%p\n", &array);printf("array=%p\n", array);printf("\n");// 以下 等价printf("array[1]=%p\n", array[1]);printf("*(array+1)=%p\n", *(array+1));printf("**(array + 1)= %d\n", **(array + 1));return 0;
}

运行结果:

*(array +1)+3  == &array[1][3] //第二行,第三个元素的地址

 printf("array[1][2]=%p\n", &array[1][2]);printf("*(array+1)+2=%p\n", *(array + 1)+2);printf("array[1][2]=%d\n", array[1][2]);printf("*(*(array + 1)+2)= %d\n", *(*(array + 1)+2));

运行结果:

结论:多纬数组是一纬数组的线性扩展……;

用上面提到的“语法糖”进行理解;数组名为指针变量,然后用偏移法进行表示;

数组指针 与 二维数组  与上面16、指针数组 和 数组指针 联系起来一块学习!

#include <stdio.h>int main()
{int array[2][3] = { {0, 1, 2}, {3, 4, 5} };// 数组指针int(*p_int)[3]=array; // 或者:int(*p_int)[3]=&array;// 求地址 的表达方式printf("*(p_int+1)+2=%p\n",*(p_int+1)+2); //数组指针printf("*(array+1+2=%p\n", *(array + 1) + 2); // 数组名为指针变量printf(" &array[1][2]=%p\n", &array[1][2]); //典型的求地址printf("\n");//求 值 的表达方式printf("*(*(p_int+1)+2)=%d\n", *(*(p_int+1)+2));printf("(*(array+1)+2)=%d\n", *(*(array+1)+2));printf("array[1][2]=%d\n", array[1][2]);printf("\n");return 0;
}

运行结果:

总结:

#if 1#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {// 定义一个:字符数组char str[20] = "ABCDE";char *p_ch;p_ch = str;printf("输出字符串:%s\n", str);/* 知识点1*///字符数组的元素:通过指针法printf("*p_ch=%c\n", *p_ch);printf("*(p_ch+1)=%c\n", *(p_ch + 1));printf("*(p_ch+1)=%c\n", *(p_ch + 2));//小插曲,如果按照下面的语句输出:*p_ch+1printf("*p_ch+1=%c\n", *p_ch + 1);// 即将字符'A'+1,为'B'/* 知识点2*///指针指向的是数组第一个元素的地址;int j;for (j = 0; j < 5; j++) {printf("%c ",*(p_ch+j));}printf("\n");/* 知识点3*///定义一个:指针数组;即数组每一个元素都是指针变量const char *p1_ch[5] = {"Hello World","Hello Beijing","Hello People","Welcome Friends","So Beautiful"};int i;printf("读取指针数组的元素\n");for (i = 0; i < 5; i++) {printf("%s\n", p1_ch[i]); //因为读取字符串,直接读取地址就可以,见第11行;}/* 知识点4*///数组指针,是一个指向数组的指针;指针每±1,则意味着以数组大小移动;而不是其单个数组元素移动int a_int[5] = {1,2,3,4,5};//此处是将数组首地址赋值给数组指针int(*p_int)[5] = &a_int; //或者:int(*p_int)[5] = a_int;int k;for (k = 0; k < 5; k++) {printf("%d\t",*(*p_int+k)); //此处,*p_int 为 &a_int}printf("\n");return 0;
}#endif

请问 str[3] 和 *(str + 3) 是否完全等价?Y7ha*'qFON
hGA'Qrb XP0SB`|?I9o2f
答:完全等价。Powered by bbs.fishc.com
解析:在 C 语言中,数组名是被作为指针来处理的。更确切的说,数组名就是指向数组第一个元素的指针,而数组索引就是距离第一个元素的偏移量。这也解释了为什么在 C 语言中数组下标是从 0 开始计数的,因为这样它的索引可以对应到偏移量上。因此,str[3] 和 3[str] 是相同的,因为它们在编译器的内部都会被解释为 *(str + 3)。

如果数组 array 的定义如下:L-B_JPzj

int array[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

请问 array 和 &array 有区别吗?!IA|i8Xt
O!`cU4-,e.&ws[DdNGqlhTCBP7~H
答:有。版权属于:bbs.fishc.com
L=a{SYBZE3>dVMc;<p!N%Ciy7+P:
解析:虽然 array 和 &array 的值相同,但含义是不一样的。array 表示数组第一个元素的位置,而 &array 表示的是整个数组的位置(这里将数组看做一个整体)。

如果有 int a[10];,请问 &a 和 &a[0] 表达式的类型有何不同?y6:`<
;<5vHXRjZY?a%MsJ{Wn4hl
答:a 是一个数组,在 &a 这个表达式中,数组类型做左值,&a 表示取整个数组的首地址,类型是 int (*)[10];&a[0] 则表示数组 a 的第一个元素的首地址,虽然两个地址的数值相同,但后者的类型是 int *。@

[课后作业] S1E23:指针数组和数组指针 | 课后测试题及答案

[课后作业] S1E24:指针和二维数组 | 课后测试题及答案

18、void 指针 和 NULL 指针

void指针 为 通用指针,就是可以指向任意类型的数据。也就是说,任何类型的指针都可以赋值给void指针;

NULL指针 visual studio中,定义如下:

#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif
#endif

当你含不清楚将指针初始化为什么地址时,请将它初始化NULL;

当你对指针进行解引用时,先检查该指针是否为NULL。

注意分清楚 NULL 与 NUL:

NULL 用于指针和对象,表示指向一个不被使用的地址;

而NUL 即 ‘\0’表示字符串的结尾。

你猜下边代码会打印多少?}u

#include <stdio.h>int main()
{void a;printf("%d\n", sizeof(a));return 0;
}

答:会报错!那 void 既然是无类型,我们就不应该用它来定义一个变量,如果你一定要这么做,那么程序就会给你报错(我只是换个提问方式,看看多少童鞋会上当)。5uJt^

1. 那 sizeof(void *) 呢?O6]|JjM'^,
&9?]N1i C*{p$Wq=3KD#LbI>Uv`.u
答:如果你回答是 4 个字节或 8 个字节,那么本题不能算你答对。因为指针的尺寸是与编译器的目标平台相关的。比如目标平台是 32 位的,那么 sizeof(void*) 就是 4,如果是 64 位的,那么 sizeof(void *) 就是 8,如果是 16 位的,那么就是 2 啦。J

2.对 NULL 指针进行解引用,结果是什么?SIA}6?R^
A]kS`'p+ ?Rr9@wNu$H&"aB
答:报错。无论什么操作系统,对空指针进行解引用都是非法的。

3.请问下边定义有没有问题?={(5U

……
int *p = void *0;
……

答:报错。

NULL的宏定义为:

#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif
#endif

如果为.c文件:

这里是将 0 强制转换成 void 指针,所以要这么写才能通过编译:

int *p = (void *)0;

[课后作业] S1E25:void指针和NULL指针 | 课后测试题及答案

19、指向指针的指针

[课后作业] S1E26:指向指针的指针 | 课后测试题及答案

参考16、17的知识点;

如果要使用指针来指向二维数组,只能使用数组指针。重点!

指针数组 和 指向指针的指针。

指针数组

字符串指针数组的使用;

https://zhuanlan.zhihu.com/p/36791383

使用指向指针的指针来指向数组指针,至少有两个优势:

  • 避免重复分配内存(虽然我们进行了多个分类,但每本书的书名只占据一个存储位置,没有浪费)
  • 只需要进行一处修改(如果其中一个书名需要修改,我们只需要修改一个地方即可)

这样,代码的灵活性和安全性都有了显著地提高。

[知识点备忘] S1E26:指向指针的指针

20、常量 和 指针

指向常量的指针:

#include <stdio.h>int main()
{int num = 520;const int cnum = 880; //常值变量const int *pc = &cnum; //指向常量的指针printf("cnum: %d, &cnum: %p\n", cnum, &cnum);printf("*pc: %d, pc: %p\n", *pc, pc);pc = &num;printf("num: %d, &num: %p\n", num, &num);printf("*pc: %d, pc: %p\n", *pc, pc);num = 1024;printf("*pc: %d, pc: %p\n", *pc, pc);return 0;
}
  • 指针可以修改为指向不同的常量;
  • 指针可以修改为指向不同的变量;
  • 可以通过解引用来读取指针指向的数据;
  • 不可以通过解引用修改指针指向的数据;

注意当 常值变量和指向常值变量的指针   与  常值变量和指向变量的指针 有区别?

常量指针:

1、指向非常量的常量指针:

  • 指针自身不可以被修改,即指向的位置不可变;
  • 指针指向的值可以被修改;

2、指向常量的常量指针:

  • 指针自身不可以被修改;
  • 指针指向的值也不可以被修改;
#include <stdio.h>int main()
{int num = 520;int num2 = 1024;int * const p = &num; //常量指针*p = 1024;printf("*p: %d\n", *p);//p = &cnum;p = &num2; printf("*p: %d\n", *p);const int cnum = 880; //常值变量const int *const p2 = &cnum;printf("%d\n",*p2);return 0;
}

请问下边代码段中,A、B 和 C 中哪一个语句是错误的?你从中总结出了什么道理?4B

A:

……const int num = 520;int *p = &num;
……

B:

……int num = 520;const int *p = &num;
……

C:

……const int num = 520;const int *p = &num;
……

D:

……const int num = 520;const int * const p = &num;
……

答:只有 A 是错误的做法!在赋值、初始化或参数传参的过程中,赋值号左边的类型应该比右边的类型限定更为严格,或至少是同样严格。在 A 中,使用指针 p,就可能间接地绕过 const 设定的防线。sCl

请问下面代码可以成功通过编译并运行吗?2.&'dt<3>
RZ$|i,=a(QpK%m?^Dd.Ec

  • 如果可以,将打印什么内容?
  • 如果不可以,请说明理由。

代码:

#include <stdio.h>int main()
{const int num = 520;int *p = &num;printf("num = %d\n", num);*p = 1024;printf("num = %d\n", num);return 0;
}

答:虽然会“友情提示”,但代码还是可以通过编译并运行的。E{b`

1>d:\cppworkspace\c_again\exercise\exercise\s1e27.c(115): warning C4090: “初始化”: 不同的“const”限定符

很多鱼油不经要问:num 明摆着是一个 const 的变量,为何还可以通过指针进行间接地修改呢?'[GN2%kSwM
Xp5])0K=cwQlV|*!>L@b-B3U+v4uTZ
因为 const 其实只是对变量名(num)起到一个限制作用,也就是说你不可以通过这个变量名(num)修改它所在的内存;

但是可以通过指针进行间接修改。但是,这并不是说这块内存就不可以修改了,如果你可以通过其他形式访问到这块内存,还是可以进行修改的。所以,尽管编译器发现苗头不对,但它也只能义务地提醒你而已。&

现在请编写一个程序,测试一下你当前的计算机是大端还是小端? 大端和小端?[

#include <stdio.h>int main(void)
{int num = 0x12345678;unsigned char *p = (unsigned char *)&num;if (*p == 0x78){printf("您的机器采用小端字节序。\n");}else{printf("您的机器采用大端字节序。\n");}printf("0x12345678 在内存中依次存放为:0x%x 0x%x 0x%x 0x%x\n", p[0], p[1], p[2], p[3]);return 0;
}

运行结果:

C语言知识点汇总之一(全)相关推荐

  1. ES6知识点汇总(全)

    ES6知识点汇总(全) 近期在整理ES6相关开发文档,针对ES6新的知识点,以问答形式整理一个全面知识和问题汇总. 一.问:ES6是什么,为什么要学习它,不学习ES6会怎么样? 答: ES6是新一代的 ...

  2. ES6面试、复习干货知识点汇总(全)

    近期在复习ES6,针对ES6新的知识点,以问答形式整理一个全面知识和问题汇总.(全干货,适合对ES6有一定理解的同学复习,以及ES6面试.) 一.问:ES6是什么,为什么要学习它,不学习ES6会怎么样 ...

  3. ES6面试 知识点汇总(全)

    近期在复习ES6,针对ES6新的知识点,以问答形式整理一个全面知识和问题汇总.(全干货,适合对ES6有一定理解的同学复习,以及ES6面试.) 一.问:ES6是什么? 答: ES6是新一代的JS语言标准 ...

  4. C语言知识点汇总(1)

    C语言最重要的知识点 总体上必须清楚的: 1)程序结构是三种: 顺序结构 .选择结构(分支结构).循环结构. 2)读程序都要从main()入口, 然后从最上面顺序往下读(碰到循环做循环,碰到选择做选择 ...

  5. 2022C语言知识点大全【详细、必备】

    C语言期末必背知识点汇总[全] C语言最重要的知识点(一)<图片彩版> <C语言程序设计>必背基本知识点(二) C语言程序设计复习资料(三) C语言最重要的知识点(一)< ...

  6. 2017二级c语言成绩,2017全国计机等级考试二级C语言知识点超全整(打印版).docx

    2017全国计机等级考试二级C语言知识点超全整(打印版) 全国计算机 等级考试 目录 第一部分 公共基础知识 第 1 章 算法与数据结构1 HYPERLINK \l "_TOC_250068 ...

  7. C语言基础知识点汇总(一)

    C语言基础知识点汇总(一) 1.读程序都要从main()入口, 然后从最上面顺序往下读(碰到循环做循环,碰到选择做选择),有且只有一个main函数. 2.程序中基本的控制结构是三种: 顺序结构 .选择 ...

  8. C语言基础知识点汇总(1)

    C语言基础知识点汇总(1) 1.读程序都要从main()入口, 然后从最上面顺序往下读(碰到循环做循环,碰到选择做选择),有且只有一个main函数. 2.程序中基本的控制结构是三种: 顺序结构 .选择 ...

  9. 单片机c语言除法,单片机C语言知识点全攻略

    单片机C语言知识点全攻略 为了让有需要的朋友进一步了解单片机C语言知识,下面的文章将为大家讲解单片机C语言的知识内容,欢迎学习! C51运算符和表达式 运算符,双目运算符和三目运算符.单目就是指需要有 ...

最新文章

  1. android setAlpha 与 getBackground().setAlpha
  2. 为 ASP.NET Datagrid 创建自定义列
  3. Nginx反向代理为什么能够提升服务器性能?
  4. 如何自动填充网页表单_iCab for Mac(web网页浏览器)
  5. ROS学习总结一ROS组织框架与几个关键词
  6. 第九十六期:JavaScript 中的 4 个相等比较算法的介绍
  7. java爬去赶集,爬取赶集网二手物品下所有物品的信息
  8. jaccard相似度_推荐系统中常用计算相似度的方法和工具
  9. 软件开发高手须掌握的4大SQL精髓语句(综合篇)
  10. asp.net上传图片,上传图片
  11. chown无效的用户mysql_求大神指引,安装MYSQL时执行chown指令出错,已经创建组和用户。local中并没有找到mysql。用find找过路径...
  12. 单循环赛制php,告别东西部分组LPL实行常规赛单循环赛制
  13. 遗传算法基本原理及在互联网中的应用
  14. fgo服务器中断020202,fgo2.6版本更新 8月18日更新维护内容一览
  15. 使用APICloud AVM多端框架开发课程表功能
  16. 深度互学习-Deep Mutual Learning:三人行必有我师
  17. Angular:升级Angular 13到Angular 14
  18. WINDOWS中hosts文件位置
  19. Python之九宫格输入
  20. 广东省第三届职业技能大赛网络安全项目模块B

热门文章

  1. 2021-03-11 舒尔特方格 python3 pyqt5
  2. (C语言)找出一个二维数组中的鞍点,即该位置上的元素在该行最大,在该列上最小,也可能没有鞍点。存在则输出坐标;否则输出 -1.
  3. 文件在线预览doc,docx转换pdf(一)
  4. 北京交通大学计算机学院保研,北京交通大学保研情况怎么样,保研率高不高
  5. Android实用技巧.动画效果(二)
  6. 【小白福利—初级DBA入门必看】MySQL常用工具介绍(十)——客户端工具MySQL_slap
  7. IIS7应用程序池停止后,如何自动启动
  8. 一些关于三角函数的公式
  9. 树莓派4B安装官方发布64位 Raspberry Pi OS 系统
  10. appium java 虫师_利用 IPython 来学习 Appium