Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Code2NZH/C_Code_Learn

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

[TOC]

C语言学习笔记

第一个C语言代码

  1. C语言中一定要有main函数(主函数)

  2. printf是专门用来打印数据的一个库函数

  3. std - 标准

    i - input

    o - output

  1. C语言规定

    main函数是程序的入口

    main函数有且仅有一个

标准主函数的写法

#include <stdio.h>

int main()
{
	printf("Hello World!\n");
	return 0;
}

C语言的数据类型

计算机中的单位

计算机中的单位

C语言常见数据类型

C语言内置:

数据类型 中文名称 所占字节
char 字符型 1
short 短整型 2
int 整型 4
long 长整型 4
long long 更长的整形 8
float 单精度浮点型 4
double 双精度浮点型 8

创建:

  • struct结构体

  • 枚举

C语言规定

sizeof(long) >= sizeof(int)

注:sizeof用于获取某个数据类型所占用空间的字节数

打印各数据类型所占的字节数

#include <stdio.h>

int main()
{
	printf("char:     %zu\n", sizeof(char));
	printf("short:    %zu\n", sizeof(short));
	printf("int:      %zu\n", sizeof(int));
	printf("long:     %zu\n", sizeof(long));
	printf("long long:%zu\n", sizeof(long long));
	printf("float:    %zu\n", sizeof(float));
	printf("double:   %zu\n", sizeof(double));

	return 0;
}

/*输出结果如下:
	char:     1
	short:    2
	int:      4
	long:     4
	long long:8
	float:    4
	double:   8
*/

类型的使用

类型是用来创建变量(本质是向内存申请空间

char ch = 'w';
int high = 200;
double price = 12.3;

变量和常量的概念

变量

变量的命名

  1. 有意义
  2. 必须由字母、数字或下划线组成,不能有特殊字符
  3. 不能以数字开头
  4. 不能和C语言内置关键字同名

变量的分类

  1. 局部变量

    { }内部定义的变量

  2. 全面变量

    { }外部定义的变量

    举例

    #include <stdio.h>
    
    int b = 2;//全局变量
    int main()
    {
    	short a = 1;//局部变量
    
    	return 0;
    }

​ 当局部变量全局变量命名相同时,局部优先

示例代码--->计算两个整数的值

scanf 是一个输入函数

printf 是一个输出函数

#define _CRT_SECURE_NO_WARNINGS	//VS中使用scanf时需要加入
#include <stdio.h>
int main()
{
	int num1 = 0;
	int num2 = 0;//初始化

	//输入两个整数
	scanf("%d %d",&num1,&num2);

	//求和
	int sum = num1 + num2;

	//输出
	printf("%d\n",sum);
	return 0;
}

scanf_s是VS编译器中的非标准C函数

1. 变量的作用域

  1. 局部变量的作用域

    局部变量所在的局部范围

  2. 全局变量的作用域

    整个工程

    extern---->声明来自外部的变量

    eg :extern int a;

2. 变量的生命周期

  1. 局部变量的生命周期

    进入作用域生命周期开始,出作用域生命周期结束

    #include <stdio.h>
    int main()
    {
    		{//----------------------------|----生命周期开始
    				int a = 10;
    				printf("%d\n", a);
        }   //----------------------------|----生命周期结束
    		printf("%d\n",a);
    		return 0;
    }
  2. 全局变量的生命周期

    整个程序的生命周期

常量

  1. 字面常量

    例如:

    	10;		//字面常量
    	3.14;	//字面常量
    	'w';	//字符常量
    	'abc';//字符串常量
  2. const修饰的常变量

    在C语言中,const修饰的a本质是变量,但不能被修改,具有常量的属性

    #include <stdio.h>
    int main()
    {
    	const int a = 10;//----------------------此处的a不能被修改
    	a = 20;//------------err-此句无效,此处的变量a无法修改
    	printf("%d",a);
    
    	return 0;
    }

    const相当于锁定了整个变量,使其不能再变化

  3. define定义的标识符常量

    #include <stdio.h>
    
    #define MAX 100
    int main()
    {
    	printf("%d\n", MAX);
    	int a = MAX;
    	printf("%d\n", a);
    	return 0;
    }
  4. 枚举常量

    #include <stdio.h>
    
    //枚举常量
    enum Color
    {
    	RED,GREEN,BLUE
    };
    
    int main()
    {
    	//三原色
    	//RED,GREEN,BLUE
    	enum Color c = RED;
      RED = 10;//-----------------------err-枚举常量不能修改
    
    	return 0;
    }

字符串+转义字符+注释

一、字符串

char字符类型

''包裹的为字符

" "包裹的为字符串

eg :

  1. 定义数组

    char arr1[]="abcdef";//占用7个字节

    ⭐ 在字符串末尾会自动加上\0作于结束标志限char型数组

    printf("%d\n", arr1);结果为

    abcdef
  2. 定义数组

    char arr2[] = {'a','b','c','d','e','f'};//占用6个字节

    printf("%d\n", arr2);结果为

    abcdef烫烫烫烫烫烫烫烫烫烫烫狓oa?

    只有当程序遇到\0才会结束打印

    char arr2[] = {'a','b','c','d','e','f','\0'};

    在数组末尾加上一个\0,打印的结果才为abcdef

strlen()该函数可返回字符串长度 (string length) , 包含在头文件 string.h

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

int main()
{
	char arr1[] = "abcdef";//7(以\0结尾)
	char arr2[] = {'a','b','c','d','e','f'};//6

	printf("%d\n", strlen(arr1));//打印arr1的长度
	printf("%d\n", strlen(arr2));//打印arr2的长度

	return 0;
}

运行结果 : arr1:6 arr2:33--->由于arr2[ ]后没有停止标志位\0,因此打印出数组的长度为随机值

  • 若改为char arr2[] = {'a','b','c','d','e','f','\0};后:arr2:6

  • 若改为char arr2[10] = {'a','b','c','d','e','f'};数组中多出的位将会用 0 来填充,此时:arr2的长度为6

  • C99标准之前,数组的大小都是用常量或者常量表达式来指定
int arr1[10]={0};
int arr2[3+7]={1,2,3,4};
  • C99标准之后,支持变长数组,此时允许数组的大小是变量,这种指定方式的数组不能初始化
int a = 10;
int arr1[a];

VS不支持C99标准

二、转义字符

\转义序列符

转义字符 释义
? 在连续书写多个问号时使用,防止它们被解析成三字母词
\` 用于表示字符常量
\" 用于表示一个字符串内部的双引号
\\ 用于表示一个反斜杠,防止它们被解释成为一个转义序列符
\a 警告字符,蜂鸣
\b 退格符
\f 进纸符
\n 换行
\r 回车
\t 水平制表符
\v 垂直制表符
\ddd ddd表示1~3个八进制的数字。如:\130--->X
\xdd dd表示2个十六进制数字。如:\x30--->0
  • 转义字符只占一个字符
  • 三字母词:??)会在某些编译器中解析成]
  • \ddd - 八进制转十进制后的ASCII码值
  • \xdd - 该十六进制的所代表的ASCII码值

ASCII码表

[转到表尾](#### %x)

二进制 十进制 十六进制 字符/缩写 解释
0 0 00 NUL (NULL) 空字符
1 1 01 SOH (Start Of Headling) 标题开始
10 2 02 STX (Start Of Text) 正文开始
11 3 03 ETX (End Of Text) 正文结束
100 4 04 EOT (End Of Transmission) 传输结束
101 5 05 ENQ (Enquiry) 请求
110 6 06 ACK (Acknowledge) 回应/响应/收到通知
111 7 07 BEL (Bell) 响铃
1000 8 08 BS (Backspace) 退格
1001 9 09 HT (Horizontal Tab) 水平制表符
1010 10 0A LF/NL(Line Feed/New Line) 换行键
1011 11 0B VT (Vertical Tab) 垂直制表符
1100 12 0C FF/NP (Form Feed/New Page) 换页键
1101 13 0D CR (Carriage Return) 回车键
1110 14 0E SO (Shift Out) 不用切换
1111 15 0F SI (Shift In) 启用切换
10000 16 10 DLE (Data Link Escape) 数据链路转义
10001 17 11 DC1/XON(Device Control 1/Transmission On) 设备控制1/传输开始
10010 18 12 DC2 (Device Control 2) 设备控制2
10011 19 13 DC3/XOFF(Device Control 3/Transmission Off) 设备控制3/传输中断
10100 20 14 DC4 (Device Control 4) 设备控制4
10101 21 15 NAK (Negative Acknowledge) 无响应/非正常响应/拒绝接收
10110 22 16 SYN (Synchronous Idle) 同步空闲
10111 23 17 ETB (End of Transmission Block) 传输块结束/块传输终止
11000 24 18 CAN (Cancel) 取消
11001 25 19 EM (End of Medium) 已到介质末端/介质存储已满/介质中断
11010 26 1A SUB (Substitute) 替补/替换
11011 27 1B ESC (Escape) 逃离/取消
11100 28 1C FS (File Separator) 文件分割符
11101 29 1D GS (Group Separator) 组分隔符/分组符
11110 30 1E RS (Record Separator) 记录分离符
11111 31 1F US (Unit Separator) 单元分隔符
100000 32 20 (Space) 空格
100001 33 21 !
100010 34 22 "
100011 35 23 #
100100 36 24 $
100101 37 25 %
100110 38 26 &
100111 39 27 '
101000 40 28 (
101001 41 29 )
101010 42 2A *
101011 43 2B +
101100 44 2C ,
101101 45 2D -
101110 46 2E .
101111 47 2F /
110000 48 30 0
110001 49 31 1
110010 50 32 2
110011 51 33 3
110100 52 34 4
110101 53 35 5
110110 54 36 6
110111 55 37 7
111000 56 38 8
111001 57 39 9
111010 58 3A :
111011 59 3B ;
111100 60 3C <
111101 61 3D =
111110 62 3E >
111111 63 3F ?
1000000 64 40 @
1000001 65 41 A
1000010 66 42 B
1000011 67 43 C
1000100 68 44 D
1000101 69 45 E
1000110 70 46 F
1000111 71 47 G
1001000 72 48 H
1001001 73 49 I
1001010 74 4A J
1001011 75 4B K
1001100 76 4C L
1001101 77 4D M
1001110 78 4E N
1001111 79 4F O
1010000 80 50 P
1010001 81 51 Q
1010010 82 52 R
1010011 83 53 S
1010100 84 54 T
1010101 85 55 U
1010110 86 56 V
1010111 87 57 W
1011000 88 58 X
1011001 89 59 Y
1011010 90 5A Z
1011011 91 5B [
1011100 92 5C \
1011101 93 5D ]
1011110 94 5E ^
1011111 95 5F _
1100000 96 60 `
1100001 97 61 a
1100010 98 62 b
1100011 99 63 c
1100100 100 64 d
1100101 101 65 e
1100110 102 66 f
1100111 103 67 g
1101000 104 68 h
1101001 105 69 i
1101010 106 6A j
1101011 107 6B k
1101100 108 6C l
1101101 109 6D m
1101110 110 6E n
1101111 111 6F o
1110000 112 70 p
1110001 113 71 q
1110010 114 72 r
1110011 115 73 s
1110100 116 74 t
1110101 117 75 u
1110110 118 76 v
1110111 119 77 w
1111000 120 78 x
1111001 121 79 y
1111010 122 7A z
1111011 123 7B {
1111100 124 7C
1111101 125 7D }
1111110 126 7E ~
1111111 127 7F DEL (Delete) 删除

[转到表头](### ASCII码表)

%x

%x 含义
%d 打印整形
%c 打印字符
%s 打印字符串
%f 打印float类型的数据
%lf 打印double类型的数据
%p 打印地址

三、注释

C语言的注释风格/**/

/*
这是注释
*/

不支持嵌套注释

C++的注释风格//

//这是注释

选择语句与循环语句

选择语句(分支语句)

if语句

  • 语句结构

    if(逻辑表达式)
    {
      语句体1;
    }
    else
    {
      语句体2;
    }

    ​ **解释:**如果逻辑表达式成立,就执行语句体1;否则,执行语句体2(else可以省略)

  • 应用举例

if else语句

#include <stdio.h>

int main(void) 
{
	int choose = 0;
	printf("是否将开关打开(0/1):");
	scanf("%d", &choose);
	if (choose == 1)
	{
		printf("打开\n");
	}
	else if(choose == 0)
	{
		printf("关闭\n");
	}
	else
	{
		printf("错误输入\n");
	}

	return 0;
}
if(表达式)
{
  语句列表;
}
  • 如果表达式的结果为真,则语句执行
  • 0表示假,非0表示为真
if else(表达式)
{
  语句列表;
}
  • if else语句,是紧接在if语句后的带有选择条件else语句
  • 如果表达式的结果为真,则语句执行
else
{
  	语句列表;
}
  • 一般位于if else语句的最后,作用于其它未被选择的条件,类似于集合与其补集的关系
  • else遵循就近原则,即else与离其最近的if匹配

例1:if语句实现判断奇偶数

int main()
{
	while(1)
	{ 
		int num = 0;
		printf("输入一个数:");
		scanf("%d", &num);
		if (num % 2 == 1)
			printf("%d为奇数\n", num);
		else
			printf("%d为偶数\n", num);
	}
	return 0;
}
  • 输出结果

    输入一个数1
    1为奇数
    输入一个数12
    12为偶数
    输入一个数123
    123为奇数

例2:if语句输出1-100之间的奇数

//定义一个可返回奇数的函数
int Retu_J(int Num)
{
	if (1 == Num % 2)
	{
		return Num;
	}
	else 
		return 0;
}

int main()
{
	int i = 0;
	int num = 0;
	while (i <= 100)
	{
		num = Retu_J(i);
		if (num)
		{
			printf("%d ", num);
		}
		i++;
	}
	return 0;
}
  • 输出结果

    1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97 99

    ​ 此处运用了外部函数的调用来实现函数功能,当然除此之外还有更加简便的写法,此处不过多赘述。

switch语句

switch语句也是一种分支语句,但通常用于多分支的情况。

  • 语句结构

    switch(整形变量)
    {
      case 常量1 : 语句体1 ; break;
      case 常量2 : 语句体2 ; break;
        ......
      default : 语句体x ; break;
    }

    ​ **含义:**将变量与case后的各个常量对比,若有相等,则执行相应的语句;若全不等,则执行default后面的语句。

  • switch应用举例

    int main()
    {
    	int day;
    	while (1)
    	{
    		scanf("%d", &day);
    		switch (day)
    		{
    		case 1:printf("星期一\n"); break;
    		case 2:printf("星期二\n"); break;
    		case 3:printf("星期三\n"); break;
    		case 4:printf("星期四\n"); break;
    		case 5:printf("星期五\n"); break;
    		case 6:printf("星期六\n"); break;
    		case 7:printf("星期七\n"); break;
    		default : printf("没有这一天\n"); break;
    		}
    	}
    	return 0;
    }
    • 输出结果:
    5
    星期五
    6
    星期六
    9
    没有这一天
  • switch应用举例:case穿透

    使用switch实现的代码功能:

    当输入1~5时,输出工作日;

    当输入6~7时,输出周末;

    int main()
    {
    	int day = 1;
    	while(1)
    	{ 
    		scanf("%d", &day);
    		switch (day)
    		{
    		case 1:
    		case 2:
    		case 3:
    		case 4:
    		case 5:
    			printf("工作日\n");
    			break;
    		case 6:
    		case 7:
    			printf("周末\n");
    			break;
    		default: 
    			printf("输入错误\n"); 
    			break;
    		}
    	}
    	return 0;
    }
    • 输出结果:
    1
    工作日
    6
    周末
    9
    输入错误

    case穿透可以实现多个case匹配同一个执行语句

循环语句

while循环

  • 语句结构

    while(逻辑表达式)
    {
      循环体;
    }

    ​ **解释:**如果逻辑表达式成立,则执行循环体,执行后再次判断,直至不成立时才停止执行

  • while应用举例

    *代码实现功能:*打印数字1~10;

    int main()
    {
    	int a = 1;
    	while (a <= 10)
    	{
    		printf("%d ", a);
    		a++;
    	}
    	return 0;
    }

    输出结果:

    1 2 3 4 5 6 7 8 9 10
  • break中止while循环

    break可以永久的中止整个循环

    int main()
    {
    	int a = 1;
    	while (a <= 10)
    	{
    		if (5 == a)
    			break;//此代码执行时,会直接跳出while循环
    		printf("%d ", a);
    		a++;
    	}
    	return 0;
    }
    • 输出结果:
    1 2 3 4 
  • continue跳过本次while循环

    continue被执行时,会跳过continue后面的语句(结束本次循环),直接去判断部分,进行下一次循环的判断

    int main()
    {
    	int a = 0;
    	while (a <= 9)
    	{
        a++;
    		if (5 == a)
    			continue;
    		printf("%d ", a);
    	}
    	return 0;
    }

    ​ 因此在a=5时,continue被执行,程序将直接结束本次循环,开始下一次循环,故而等于5时的a没有被打印

    • 输出结果:
    1 2 3 4 6 7 8 9 10//输出结果中的5被直接跳过
  • while循环

    #include <stdio.h>
    
    int main()
    {
    	int count = 0;
    	printf("开始:\n");
    	while (count <= 1000)
    	{
    		printf("计数:%d\n",count);
    		count++;
    	}
    	if (count > 1000)
    	{
    		printf("END\n");
    	}
    	else printf("Lost\n");
    
    	return 0;
    }
  • for循环

  • do ... while循环

C语言是结构化的程序设计语言

  • 顺序结构
  • 选择结构
  • 循环结构

函数、数组与操作符

函数

求任意两个数的和 ( 函数的调用 )

#include <stdio.h>

/************************************************
**函数名:Add	
**参数:	 整形变量a,b
**返回值:求和a+b=s
**作用:  将a+b的值返回
*/
int Add(int a, int b)
{
	return (a + b);
}

int main()
{
	int X = 1;
	int Y = 1;
	scanf("%d %d", &X, &Y);

	int sum = Add(X, Y);
	printf("和为:%d", sum);

	return 0;
}

数组

举例 : 定义一个数组

int arr[10] = {10,11,12,13,14,15,16,17,18,19};
//    数组的下标 0  1  2  3  4  5  6  7  8  9
  • 数组里可以放置一系列相同类型的元素

  • C语言规定:数组的每个元素都有下标,且数组的下标都是从0开始

  • 可以通过下标来访问数组中的元素---->arr[7] = 17

    通过while循环来打印数组中的元素

    int main()
    {
    	int arr[10] = { 10,11,12,13,14,15,16,17,18,19 };
    	int i = 0;
    	while (i < 10)
    	{
    		printf("%d ", arr[i]);
    		i++;
    	}
    	return 0;
    }

操作符

算术操作符

/

  • 除号两端都是整数时执行的是整数除法
int a = 7 / 2;
      |
     \|/
printf("%d\n", a);//结果:a = 3        
  • 除号两端有一个浮点数,就会执行浮点数的除法
float a = 7 / 2.0;//两端需要有一个位小数
        |
       \|/
printf("%.2f\n", a);//结果:a = 3.50 【%f-->表示输出浮点数;.x就保留x位小数,默认保留7位小数】

%取模

取模操作符两端必须为整数

int b = 7 % 2;
     |
    \|/
printf("%d\n", b);//结果:a = 1       

移位操作符

<<左移操作符

.

>>右移操作符

.

位操作符

&按位与

.

|按位或

.

^按位异或

.

赋值运算符

运算符 描述 实例
= 简单的赋值运算符,把右边操作数的值赋给左边操作数 C = A + B 将把 A + B 的值赋给 C
+= 加且赋值运算符 C += A 相当于 C = C + A
-= 减且赋值运算符 C -= A 相当于 C = C - A
*= 乘且赋值运算符 C *= A 相当于 C = C * A
/= 除且赋值运算符 C /= A 相当于 C = C / A
%= 求模且赋值运算符,求两个操作数的模赋值给左边操作数 C %= A 相当于 C = C % A
<<= 左移且赋值运算符 C <<= 2 等同于 C = C << 2
>>= 右移且赋值运算符 C >>= 2 等同于 C = C >> 2
&= 按位与且赋值运算符 C &= 2 等同于 C = C & 2
^= 按位异或且赋值运算符 C ^= 2 等同于 C = C ^ 2
` =` 按位或且赋值运算符

对于赋值操作符=

int a = 0;//初始化
a = 10;   //赋值

单目操作符(只有一个操作数)

运算符 描述 实例
! 逻辑反操作(0假1真) a = 10 ; !a = -10
- 负值 a = -10
+ 正值 a = +10(+可省略)
& 取地址 &a; 将给出变量的实际地址
sizeof() 返回变量的长度 sizeof(int) 将返回 4,其中 a 是整数
~ 对一个数的二进制位按位取反 ~0 = 1
-- 自减 *前置--:*先--,后使用
++ 自增 *后置++:*先使用,后--
* 间接访问操作符(解引用) *p(指针变量解引用)
(类型) 强制类型转换 c = a+(int)b
  • sizeof 是操作符

    int a = 10;
    
    printf("%d\n", sizeof(a));  //4
    printf("%d\n", sizeof(int));//4
    printf("%d\n", sizeof a);   //4--->a的括号可省,间接说明sizeof是操作符
    int arr[ 10 ] = { 0 };
    printf("%d\n", sizeof(arr));//40--->计算的是整个数组的大小,单位是字节
    printf("%d\n", sizeof(arr[0]));//4->计算的是数组中元素的大小,单位是字节
    printf("%d\n", sizeof(arr)/sizeof(arr[0]));//10

    🌔注意区分int型数组与char型数组

  • ++自增操作符

    int main()
    {
    	int a = 10;
    	int b = a++;//先使用,再++
    	printf("%d\n", a);//11
    	printf("%d\n", b);//10
    
    	return 0;
    }
    int main()
    {
    	int a = 10;
    	int b = ++a;//先++,再使用
    	printf("%d\n", a);//11
    	printf("%d\n", b);//11
    
    	return 0;
    }
  • (类型)强制类型转换

    int a = 3.14;
    //3.14字面浮点数,编译器默认理解为double类型
    int a = (int)3.14;
    //将3.14强制转换为int类型,自动舍去小数点后的数值

关系操作符

操作符 描述
> 大于
>= 大于或等于
< 小于
<= 小于或等于
!= 不等于
== 等于(区别于赋值=

逻辑操作符

  • &&逻辑与(并且)

    • 全真才为真(有假则假)

      eg:成绩分类

      int main()
      {
      	int scores;
      	while(1)
      	{
      		printf("输入成绩:");
      		scanf("%d", &scores);
          //只有scores满足逻辑与符号(&&)前后的所有条件,程序才会向下执行
      		if (scores >= 60 && scores <= 90)
      		{
      			printf("合格\n");
      		}
      		else if (scores < 60)printf("不及格\n");
      		else if (scores > 90 && scores <=100)printf("优秀\n");
      		else printf("输入错误\n");
      	}
      	return 0;
      }

      程序运行结果:

      输入成绩56
      不及格
      输入成绩78
      合格
      输入成绩95
      优秀
      输入成绩123
      输入错误
  • ||逻辑或(或者)

    • 全假才为假(有真则真)

      eg:区分1~9内的质数

      int main()
      {
      	int num;
      	while (1)
      	{
      		printf("输入1-9:");
      		scanf("%d", &num);
          //num只需满足逻辑或符号(||)前后的任一条件,程序就会向下执行
      		if (num == 2 || num == 3 || num == 5 || num == 7)
      		{
      			printf("%d 是质数\n", num);
      		}
      		else
      		{
      			printf("%d 不是质数\n", num);
      		}
      	}
      	return 0;
      }

      程序运行结果:

      输入1-91
      1 不是质数
      输入1-92
      2 是质数

条件操作符(三目操作符)

三目操作符:有三个操作数(可以嵌套

Exp1 ? Exp2 : Exp3

Exp1 Exp2 Exp3
×
×
int a = 10;
int b = 20;
int c = a > b ? a : b;//输出b=20
  • a > b ? a : b该句句意为:判断a>b是否成立,如果成立,则输出a,反之输出b

逗号表达式

  • 即用逗号,隔开的一串表达式

  • 自左向右依次计算,整个表达式的结果是最后一个表达式的结果

    int a = 10;
    int b = 20;
    int c = 30;
    //........15.......50.......65....
    int d = (a = a+5, b = b+c, a + b);
    printf("%d\n", d);//此时d的值为65

其它操作符

  • []下标引用操作符

    • 访问数组元素时,需要用到下标引用操作符

      int arr[3] = {12,23,34};
      a = arr[1];
      //a的值为23

      arr[1]中,[ ]就是下标引用操作符,arr和1就是[ ]的操作数

      注意: 数组中的元素下标是从0开始

    • 给数组中的元素赋值时,也是如此

      int arr[3] = {12,23,34};
      arr[1] = 66;
      //此时数组元素中的23被重新赋值为66
  • ()函数调用操作符

    int Sum(int x,int y)
    {
      return x+y;
    }
    int main()
    {
      int s = Sum(2,3);//此处的()就是函数调用操作符,Sum、2、3都是()的操作数
      
      return 0;
    }
  • .操作符

    结构体对象.成员名

  • ->操作符

    结构体的指针变量->成员名

常见的关键字

关键字---C语言本身内置,用户无法创建

  1. auto

    • 所有的局部变量前都会加上auto

      int main()
      {
      	int a = 10;//*****************************
        return 0;
      }

      等效于

      int main()
      {
      	auto int a = 10;//************************
        return 0;
      }

      也因此,局部变量在开始时会创建,结束时会自动销毁

  2. 循环语句

    • for
    • while
    • do while
    • break
      • 用于跳出循环
      • 常与for、while、do while、switch搭配使用
    • continue
  3. 分支语句

    • if else

    • switch

    • case

    • default

    • go to

  4. 内置类型

    • char

    • int

    • short

    • long

    • float

    • double

    • signed - 有符号

    • unsigned - 无符号

      ... ...

  5. 自定义类型

    • enum - 枚举
    • struct - 结构体
    • union - 联合体(共用体)
  6. const

    • 常属性
    • 用于修饰变量,不能被修改
  7. register - 寄存器

    • 寄存器关键词

      int main()
      {
      	register int num = 1;//建议:num = 1存放到寄存器中
                                  
      	return 0;
      }
      • 对于需要反复使用的变量放到寄存器中,会提升程序的运行速度

        电脑储存

  8. extern

    • 用于声明外部符号
  9. static

    • 在C语言中,static可以用来修饰变量或函数

        1. 修饰局部变量(改变存储位置

          int add()
          {
          	int a = 1;
          	printf("%d ", a);
          	a++;
          }
          
          int main()
          {
          	int i = 0;
          	while (i < 10)
          	{
          		add();
          		i++;
          	}
          
          	return 0;
          }

          以上代码的输出结果为:

          1 1 1 1 1 1 1 1 1 1 
          • 这是由于局部变量只会在其进入作用域时创建,出作用域时销毁。在主函数每次循环时,函数add()始终处于刚刚创建的状态,因此每次打印出的a的值都为1

          使用static来修饰a,使其成为静态变量

          int add()
          {
          	static int a = 1;//静态变量在出作用域时不销毁,每次循环延续使用上次a的值
          	printf("%d ", a);
          	a++;
          }
          
          int main()
          {
          	int i = 0;
          	while (i < 10)
          	{
          		add();
          		i++;
          	}
          
          	return 0;
          }

          此时,代码的运行结果为:

          1 2 3 4 5 6 7 8 9 10
          • 通过static修饰的局部变量,出了作用域,不会被销毁。

          • 本质上,static修饰局部变量的时候,改变了变量的存储位置(由栈区变为静态区)。同时延长了变量的生命周期,使其变得和程序的生命周期一样长。

          C语言内存区域划分

        2. 修饰全局变量(改变全局变量的链接属性

          • 全局变量具有外部链接属性static修饰全局变量时,该全局变量的外部链接属性就变成了内部链接属性。其它源文件就不能再通过extern来使用这个全局变量

          text.c

          #define _CRT_SECURE_NO_WARNINGS
          
          //全局变量--->全局变量具有外部链接属性
          //int number = 1234;
          static int number = 1234;
          //此时的全局变量number只具有内部连接属性

          main.c

          extern int number;
          int main()
          {
          	printf("%d\n", number);
          	return 0;
          }
          • 此时就无法使用extern int number;来声明外部变量number
        3. 修饰函数(改变函数的链接属性

          类似于修饰全局变量,函数同样具有外部链接属性static修饰函数时,该函数的外部链接属性就变成了内部链接属性。其它源文件就不能再通过extern来使用这个函数

          text.c

          //此时在main.c中可以通过extern外部调用Add函数
          int Add(int x, int y)//具有外部链接属性
          {
          	return x + y;
          }
          //*****************************************
          //用static修饰的函数Add,无法在main.c中使用
          static int Add(int x, int y)//仅具有内部连接属性
          {
          	return x + y;
          }

          main.c

          extern int Add(int x, int y);
          
          int main()
          {
          	int a = 10;
          	int b = 20;
          	int c = Add(a, b);
          	printf("%d\n", c);
          	return 0;
          }
  10. return

  • 函数返回值
  1. sizeof

    • 计算大小(字符或字符串长度)

      int main()
      {
      	int a = 10;
      	char b = 20;
      	double c = 30;
      	                                
      	printf("%d\n", sizeof a);//8
      	printf("%d\n", sizeof b);//1
      	printf("%d\n", sizeof c);//4
                                      
      	return 0;
      }
      • sizeof是根据所定义变量的类型,从而计算出变量的大小,例如:int型变量a的大小为4;char型变量b的大小为1;double型变量c的大小为8
  2. typedef

    • 类型定于(类型重命名)

      • 可以用于简化一些复杂的类型

        typedef unsigned int	uint;
        //将unsigned int重定义为uint
        typedef unsigned char uchar;
        //将unsigned char重定义为uchar

        简化结构体的名称:

        typedef struct Node
        {
        	int data;
        	struct Node* next;
        }Node;
                                                                                                        
        int main()
        {
        	struct Node b;
        	Node a;
                                                                                                        
        	return 0;
        }
  3. void

    • 常用于函数的返回类型,函数的参数
  4. volatile

  5. #define

    • 定义常量

      1. 定义常量

        #define Sum 100
        //此时的Sum等效于常量100
      2. 定义宏

        #define ADD(x,y) ((x)+(y))
        //ADD--------->宏名
        //(x,y)------->宏的参数(参数为无类型)
        //((x)+(y))--->宏体
                                                                                                    
        int main()
        {
        	int a = 10;
        	int b = 20;
        	int c = ADD(a, b);//进行宏替换后的结果为((a)+(b))
                                                                                                      
        	printf("%d\n", c);
        	                                                                                            
        	return 0;
        }
        ADD--------->宏名
        (x,y)------->宏的参数(参数为无类型)
        ((x)+(y))--->宏体

注:define不是关键字,而是预处理指令

指针

  • 指针变量用于存放地址

例如在32位的电脑

  • 在计算机中,内存会划分为一个个的内存单元(一个内存单元的大小为:1byte)

  • 每个内存单元都有一个编号

32位的电脑中,有32根地址线

00000000 00000000 00000000 00000000		-->0
00000000 00000000 00000000 00000001		-->1
00000000 00000000 00000000 00000010		-->2
00000000 00000000 00000000 00000011		-->3
... ...
11111111 11111111 11111111 11111111		-->4,294,967,295

$ 2^{32}=4,294,967,295(Byte) $ 个地址序列 $$ \begin{aligned} 2^{32} &=4,294,967,295(Byte)\ &=4,194,304(Kb)\ &=4096(Mb)\ &=4(Gb) \end{aligned} $$

指针

int main()
{
	int a = 11;//向内存申请4个字节,存储11

	printf("%p\n", &a);//%p取地址操作符
	return 0;
}
  • 相较于64位的系统,在VS中监视变量a的存储过程

    ![0000000b](D:\文档\新建文件夹 (2)\C语言程序设计\源码\画图\0000000b.png)

  • a存放的地址:

    监视a

  • 在内存中a的数据为

    0x00000005B9CFFBA4  0b 00 00 00  ....
                        ^^ ^^ ^^ ^^
                        || || || ||
                        A4 A5 A6 A7
  • 若要存储a的地址:

    int* p = &a;
    printf("%p\n", &a);//%p用于打印变量地址

    此时的p就是指针变量

    int* p = &a;
                                                                        
    int--->说明p指向的对象是int类型的
    *  --->说明p是指针变量
    • 内存单元

      编号--->地址--->地址也被称为指针

      因此存放指针(地址)的变量就是指针变量

int main()
{
	int a = 11;
	int* p = &a;
	*p = 20;//解引用操作符,意思就是通过p中存放的地址,找到p所指向的对象,*p就是p指向的对象
	printf("%d\n", a);//20
	return 0;
}
  • *p = 20;
    • *p解引用操作符,意思就是通过p中存放的地址,找到p所指向的对象,*p就是p指向的对象(此处的p指向的对象是a)

指针变量的大小

//x64的环境下
int main()
{
	printf("%zd\n", sizeof(char*));		//8
	printf("%zd\n", sizeof(short*));		//8
	printf("%zd\n", sizeof(int*));		  //8
	printf("%zd\n", sizeof(double*));	//8

	return 0;
}
  • 指针变量是用来存放地址的
  • 不管是什么类型的指针,都是在创建指针变量
  • 指针变量的大小取决于系统存放一个地址需要多大的空间
  • *32位(x86)*的系统上的地址:32bit - 4byte,所以此处指针变量的大小是4个字节
  • *64位(x64)*的系统上的地址:64bit - 8byte,所以此处指针变量的大小是8个字节

C指针(嵌入式)

🔺指针(Pointer)

指针与底层硬件(内存)联系紧密,使用指针可以操作数据的地址,实现数据的间接访问

🔺计算机储存机制

  • 计算机存储机制
    • 注:与小端存储相对应的还有大端存储,即大端在首地址

🔺定义指针

  • 指针即指针变量,用于存放其它数据单元(变量、数组、结构体、函数等)的首地址

    • 若指针存放了某个数据单元的首地址,则这个指针指向了这个数据单元

    • 若指针存放的值是0,则该指针为空指针

  • 定义一个指针变量

    数据类型 所占字节 指向该数据类型的指针 所占字节
    (unsigned) char 1字节 (unsigned) char * x字节
    (unsigned) short 2字节 (unsigned) short * x字节
    (unsigned) int 4字节 (unsigned) int * x字节
    (unsigned) long 4字节 (unsigned) long * x字节
    float 4字节 float * x字节
    double 8字节 double * x字节
    • 16位系统 x= 2

    • 32位系统 x= 4

    • 64位系统 x= 8

      指针占用的位宽 = 系统的位宽

🔺指针的操作

int a; 定义一个int类型的数据
int *p; 定义一个指向int类型数据的指针p
  • 对指针p有如下操作方式

    操作方式 举例 解释
    取地址 p = &a; 将数据a的首地址赋给p
    取内容 *p; 取出指针指向的数据单元
    加++ p++; 使指针向下移动1个数据宽度
    p = p+5; 使指针向下移动5个数据宽度
    减-- p--; 使指针向上移动1个数据宽度
    p = p-5; 使指针向上移动5个数据宽度

    *数据宽度:*int = 4 ; char = 1 ... ...

    • 变量1&变量2 ---> 按位与
    • &变量1 ---> 取地址
    • 变量1*变量2 ---> 相乘
    • *变量1 ---> 取内容(解引用)
    • int等数据类型* ---> 标识

🔺数组与指针

  • 数组是一些相同数据类型的变量组成的集合

  • 数组名为指向该数据类型的指针

    数组的定义等效于申请内存--->定义指针--->初始化

    char a[] = {0x33,0x34,0x35};
    1. 申请内存

    2. 定义char *a = 0x4000

      0x4000是数组*a[ ]*中元素的首地址

    3. 初始化数组数据

  • 利用下标引用数组数据⇔指针取内容

    数组数据 指针取内容
    a[0] *a
    a[1] *(a+1)
    a[2] *(a+2)
  • 变量指针二级指针

    同级指针之间才能相互赋值,跨级赋值将会导致编译器报错或警告

🔺指针的应用

  • 传递参数

    1. 使用指针传递大容量的参数,主函数和子函数使用的是同一套数据,避免了参数传递过程中的数据复制,提高了运行效率,减少了内存占用。
    2. 使用指针传递输出函数,利用主函数和子函数使用同一套数据的特性,实现数据的返回,可实现多返回值函数的设计。

不使用指针传递(使用值传递

#include <stdio.h>

void Fun(int Temp)
{
	printf("%x\n", Temp);
}

void main()
{
	int a = 0x66;
	Fun(a);
	return 0;
}

值传递

  • 优点 :主函数与子函数之间的数据隔离开,保证了数据的安全
  • 缺点:数据重复,占用了更多的空间,降低了程序的运行效率

指针传递数组

#include <stdio.h>
//找到数组中的最大值
//***************形参********
int FindMax(int* array, int length)
{
	int Max = array[0];
	int i;
	for (i = 1; i < length; i++)
	{
		if (array[i] > Max)
		{
			Max = array[i];
		}
	}
	return Max;
}

void main()
{
	int a[] = { 5,6,9,7,8,1 };
	int Max = 0;
  //**********实参****
	Max = FindMax(a, 6);
	printf("Max=%d\n", Max);//9
	return 0;
}

指针传递数组

指针传递输出函数

#include <stdio.h>
//输出最大值及其出现的次数
void FindMaxAndCount(int* max, int* count, const int* array, int length)
//const----->只读
{
	int i;
	*max = array[0];
	*count = 1;
	for (i = 1; i < length; i++)
	{
		if (array[i] > *max)
		{
			*max = array[i];
			*count = 1;
		}
		else if (array[i] == *max)
		{
			*count = *count + 1;
		}
	}
}

void main()
{
	int a[] = { 8,8,6,8,1,3,8 };
	int Max, Count;
	FindMaxAndCount(&Max, &Count, a, 7);
	printf("Max=%d\n", Max);
	printf("Count=%d\n", Count);
	return 0;
}

指针传递输出函数

    • 数组复制:strcpy( _ , _ )

      char str1[20];
      char str2[] = "Hello";
      strcpy(str1,str2);//将数组str2的值赋给数组str1

指针传递返回值

将模块内的共有部分返回,让主函数持有模块的“句柄”,便于程序对指定对象的操作

#include <stdio.h>
int A[] = { 21,76,49 };
int* GetA( )//返回值为int型指针
{
	return A;
}

void main()
{
	int* a;
	a = GetA();
	printf("A[0]=%d\n", a[0]);
	printf("A[1]=%d\n", a[1]);
	printf("A[2]=%d\n", *(a + 2));//有两种写法
}

输出结果:

A[0]=21
A[1]=76
A[2]=49

结构体

  • struct是结构体的关键字
  • 结构体是把复杂对象的单一类型整合在一起,例如:
    • 长方体--->长,宽,高
    • 学生------>姓名,学号,班级
//此时设定的结构体Studio相当于“图纸”,不占用内存空间
struct Student
{
	char name[20];
	int age;
	char sex[10];
	char Tel[20];
};

int main()
{
	//用类型来创建变量,此时才给stu1分配了空间
	struct Student stu1 = {"小明",20,"男","123456789"};
 
	printf("姓名:%s\n年龄:%d\n性别:%s\n电话:%s\n", stu1.name, stu1.age, stu1.sex, stu1.Tel);
  //结构体对象 . 成员名
	return 0;
}

输出的结果为:

姓名小明
年龄20
性别
电话123456789
struct Student
{
	char name[20];
	int age;
	char sex[10];
	char Tel[20];
};

此时设定的结构体Student相当于*“图纸”*,不占用内存空间

struct Student stu1 = {"小明",20,"男","123456789"};

是利用struct Student类型来创建结构体变量stu1,相当于利用“图纸”来建“房子”,因此在创建变量stu1的同时分配了一定的系统空间。

结构体指针变量 -> 成员名

结构体对象 . 成员名

  •   struct Student
      {
      	char name[20];
      	int age;
      	char tel[15];
      };
                              
      void print(struct Student* p)
      {
      	//结构体对象 . 成员名
      	printf("%s %d %s\n", (*p).name, (*p).age, (*p).tel);
                              
      	//结构体指针变量 -> 成员名
      	printf("%s %d %s\n", p->name, p->age, p->tel);
      }
                              
      int main()
      {
      	struct Student stu2 = { "李四",23,"123123123" };
      	print(&stu2);
                              
      	return 0;
      }
  •   //输出结果
      李四 23 123123123
      李四 23 123123123

About

C语言笔记

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages