当前位置:   article > 正文

C语言基础知识理论版(很详细)_c语言的理论

c语言的理论

文章目录

前述

C语言是一门编程语言,在大学中很多专业都会学习,考研中部分专业会有涉及,工作中也仍有应用。

以下是chatgpt对c语言的简单介绍:

C语言是一种广泛应用于系统编程和应用程序开发的高级编程语言。以下是对C语言的简单介绍:

  1. 发展历史:C语言由贝尔实验室的Dennis Ritchie在20世纪70年代初开发而成。它是基于早期的B语言,并在其基础上进行了改进和扩展。

  2. 特点和优势

    • 简洁而高效:C语言提供了简洁而高效的语法结构,使得程序编写和阅读相对容易。它具有较少的语法规则和关键字,使得程序员可以更自由地控制代码的行为。
    • 高度可移植性:C语言编写的程序可以在不同的计算机平台上进行编译和运行,具有较高的可移植性。这使得C语言成为系统级编程和跨平台开发的首选语言之一。
    • 强大的底层控制能力:C语言提供了丰富的底层控制能力,可以直接访问内存和硬件资源。这使得C语言在系统编程、驱动程序开发和嵌入式系统等领域得到广泛应用。
    • 丰富的函数库:C语言拥有丰富的标准函数库和第三方函数库,可以方便地进行各种操作,如文件处理、字符串处理、数学计算等。
  3. 应用领域:C语言广泛应用于系统编程、嵌入式系统开发、驱动程序开发、操作系统开发等领域。它也是其他高级编程语言(如C++、Java等)的基础语言。


需要注意的是,C语言相对较底层,需要程序员自行管理内存和资源,因此对于初学者来说可能有一定的学习曲线。但掌握C语言的基础知识可以为后续学习其他编程语言打下坚实的基础。

一、数据

编写程序的目的是处理数据,所以先要了解数据。计算机中的数据不同于我们数学课上写的数可以随意表示任何数据,计算机中的整数和小数是不一样的,它们属于不同的类型:计算中的数据是有类型的。

1.1 数据类型

数据类型四大类:基本类型、构造类型、指针类型、空类型

  1. 基本类型:字符型 char、整型 int、浮点型(单精度浮点型 float、双精度浮点型 double)
  2. 构造类型:数组型、结构体类型 struct、共用体类型、枚举类型 enum
  3. 指针类型
  4. 空类型
    在这里插入图片描述

1.2 数据

C语言中的常量、变量、表达式、函数都可以理解为数据。

第一种数据:常量

常量有四类:整型常量、实型常量、字符型常量、字符串常量。

  1. 整型常量:三种形式(十进制、八进制、十六进制)
  2. 实型常量:两种形式(小数、指数)
    • 第一形式 -> 小数形式:由数字和小数点组成。
      如:123.456 123. .456 0.456 都是合法的实型常量。
    • 第二形式 -> 指数形式:用"E"或"e"后紧跟一个整数表示以10为低的幂。
      如:1.23E-5
      注:
      字母E或e之前之后必须都有数字,且后必须是整数。
      字母E或e的前后及各数字之间不能有空格。
  3. 字符型常量:四种形式(常规、转义、八进制、十六进制)
    • 第一形式 -> 普通字符:一对单引号括一个字符
      如:‘a’ ‘A’ ‘@’ ‘9’
    • 第二形式 -> 转义字符常量:必须以一个反斜杠“\”作为开头,常用的转义字符有
      \n 表示换行符
      \t 表示制表符
      \b 表示退格符
      \r 表示回车符
    • 第三形式 -> 八进制表示的字符:\ddd 最多可以有三位八进制数
    • 第四形式 -> 十六进制表示的字符:\xhh 最多可以有两位十六进制数
  4. 字符串常量:一种形式
    定义:用一对双引号括起来若干个字符
    如:“abcdefgh” “123456” “a1b2c3”
    注:
    每一个字符串尾都有一个字符 ‘\0’
    C语言种没有字符串变量

第二种数据:变量

变量使用规则:先定义后使用
定义格式:数据类型名 变量名列表;
比如:

int i;int j;int k;
int i, j, k;
int i=1, j=1, k=9;	/*赋初值*/
  • 1
  • 2
  • 3

注:

  1. 变量必须先定义后使用。
  2. 变量名属于用户标识符,遵守用户标识符命名规则
  3. 在同一“函数体”{}中 不能定义同名变量。
  4. 同时定义多变量,必须用逗号分隔。
  5. 变量可赋初值(初始化),无初值是随机值。

第三种数据:表达式

各种表达式
定义:表达式:用运算符将数据连接起来的合法式子
运算符:三个属性:功能、优先级、结合性(运算方向(左->右 or 左<-右))
如:
3.5 + 1/2 3.5 + (1/2)
1+2+3 (3.5 * 1)/ 2 = 1.75
1.0/2 = ? 1/2 = ?

1、算术运算符及算术表达式
 + - * / %		【结合性是 左->右】
  • 1

取余(%):

  1. 两边的运算对象必须是整型
    如:7.0%3、4%3. 、不合法的表达式
  2. 求值方法:先忽略符号不计求出余数,再将所得余数的符号定成与被除数相同的符号即可。
    如:
    17%3 结果为2
    17%-3 结果为2
    -17% 3 结果为-2
    -17% -3 结果为-2
    注:
    (1)单独的常量、变量或函数调用都是C语言的合法表达式
    (2)凡是表达式都有一个确定的值
2、赋值运算符及赋值表达式

赋值运算符:= 【14级 左<-右】
格式:变量名=表达式
赋值运算符的功能:将表达式的值赋给“=”左边的变量
例如:A = 5+8 【先计算,后赋值】
a=b=c=1+2的计算顺序是a=(b=(c=(1+2))) 【表达式的值赋值给变量】
注:

  • (1)赋值运算符左边只能是一个变量
    • 如:
      a+b= 3 4=3 非法表达式
      a=6 a+(b=3) 合法的表达式
  • (2)赋值运算符右边可以是合法表达式
    • a = b = 20 a = (b=10)/(c=2)
  • (3)赋值表达式的值就是赋值运算符左边变量的值(重要)
  • (4)实型数据赋给整型变量时,实型小数舍去,整型赋给实型变量时系统自动将整型数据转成实型数据。
    • 复合赋值运算符: 【结合性:左<-右】
      += -= *= /= %= &= |= ^= >>= <<=
    • 注:复合赋值运算符的两个运算符中间不能有空格
      如:a+ =3

例1. 若有int a = 5,b=8; 则下列表达式的值是多少?用完表达式后变量a和b的值分别为多少?
a+=b/=a
表达式的值:
A的值:
B的值:

3、自增、自减运算符

自增运算符:++ i++ ++i
自减运算符:-- i-- --i
如:
i++; ++i; 等价于:i=i+1
i–; --i; 等价于:i=i-1
【自增自减运算符:结合性2:左<-右,优先级2级】
注:
(1)只能用于变量不能用字啊常量或表达式。
如 3++、(a+b)++ 不合法的表达式
如 int a=3; 则表达式的值 变量的值
a)a++ ?3 ?4
b)++a ?4 ?4
c)a-- ?3 ?2
d)-- a ?2 ?2
题1. int a=3;求a++*a

#include <stdio.h>

int main()
{
	int a = 3;
	
	int b=	a++*a;
	
	printf("a=%d\n", a);

	printf("b=%d\n",b);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

题2. 若有定义int a=20,b=20,c=20,d=20;则下列表达式的值分别为多少?
A、a++ * 10
B、10 * b++
C、++c / 7

4、逗号运算符及其表达式(‘顺序求值’表达式)

格式:表达式1,表达式2,表达式3,……,表达式n;
功能:逗号表达式的值就是表达式n的值,求值顺序是从左到右依次求解。

5、强制类型转换(显式转换)

强制类型转换(显示转换)就是手动编写代码强制性地修改数据类型。
格式:
(类型名) 表达式(类型名) (表达式)
利用强制类型转换运算符可将一个表达式的值转换成指定的类型。
如:
float x = 123.456,y;
(1) int(x) 错
(2) (int) x (int)(x)
(3) (int)x+y (int)(x+y)
进行强制类型转换,得到的是一个中间值,而原来表达式或变量的类型未发生改变。
如:
若变量x为int类型,则表达式(float)x的结果是一个单精度类型,但是x还是int型。

6、隐式转换

隐式类型转换(隐式转换)就是自动修改数据类型,一般是高精度的数据赋值给低精度的数据会出现隐式类型转换。
【隐式转换是自动完成的】
隐式类型转换

1.0/2=? 1/2=?

说明:
同一优先级的运算符,运算次序由结合方向所决定。
简单记就是:!>算术运算符 > 关系运算符 > && > || > 赋值运算符

第四种数据:函数

函数(标准函数 自定义函数)

#include<math.h>
// |-5| 如何表示?abs()		
abs(-5)
  • 1
  • 2
  • 3

1.3 标识符

标识符就是名字。这类名字分为三类,一类是关键字,一类是预定义标识符(库函数名等),一类是用户自定义标识符(函数名、变量名)。

  1. 保留字(关键字)【int char float double等 32个(C89)】
    C语言的关键字是一些具有特殊含义的保留字,它们在C语言中有着特定的用途和功能。使用时需要遵循语法规则。
  2. 预定义标识符【main】
    预定义标识符:预先定义并具有特殊含义的标识符
    scanf printf include等
  3. 用户自定义标识符【自定义的函数名、变量名】

二、顺序结构

C语言语句:三类语句
类型1 -> /**/
类型2 -> 定义语句;
类型3 -> 执行语句;

2.1 什么是顺序结构

定义:从main()开始,由上往下一条一条地执行。

三大类语句(注释、定义、执行)->执行语句有5类:

  1. 控制语句(9条)
  2. 函数调用语句;
  3. 表达式语句;
  4. 空语句;
  5. 复合语句;
    花括号算是一条语句。

顺序结构伪代码:就是第一步干什么,第二步干什么,下一步干什么…

例如:某同学3门功课的平均分? 是顺序结构
顺序结构的程序设计框架:

#include <> 
int main() 
{ 	
	输入 76 89 92	//第一步
    计算				//第二步
    输出 			//第三步
    return 0;
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

以下是chatgpt对顺序结构的解释:

在C语言中,顺序结构是一种基本的程序控制结构,它按照代码的顺序依次执行每个语句,没有任何条件或循环的控制。顺序结构是C语言程序中最简单、最基础的结构,也是程序的默认执行方式。

顺序结构的特点是代码按照从上到下的顺序依次执行,每个语句的执行不受其他因素的影响。当程序执行到一个语句时,会顺序执行该语句,然后继续执行下一个语句,直到程序结束。

以下是一个简单的顺序结构示例:

#include <stdio.h>

int main() 
{
    int a = 10;
    int b = 20;
    int sum = a + b; // 第一条语句
    printf("The sum is: %d\n", sum); // 第二条语句
    return 0; // 第三条语句 
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在上述示例中,程序按照从上到下的顺序执行。首先,声明并初始化变量a和b,然后计算它们的和并将结果存储在变量sum中。接下来,使用printf函数打印出sum的值。最后,使用return语句结束程序的执行。

顺序结构在C语言中非常常见,它使得程序可以按照预定的顺序执行各个语句,从而实现特定的功能和逻辑。

2.2 printf()输出函数

“标准输入输出函数”是以一些库函数来实现的
注意:标准函数/库函数使用前必须:#include<头文件名>再包含的程序文件中最顶部。

printf():“占位符”是以%开始的,被替换。
占位符字符:

  1. d 带符号十进制整数
  2. u 无符号十进制整数
  3. o 无符号的进制整数
  4. X或x 无符号十六进制整数
  5. c 输出一个字符,不输出单引号
  6. s 输出一个字符串。输出时不输出双引号
  7. f 输出一个实型数。隐含输出6位小数
  8. E或e 输出指数形式的实型数

在C语言中,printf函数是一个用于输出格式化字符串的函数,它可以将指定的数据以指定的格式打印到标准输出(通常是终端窗口)。printf函数属于stdio.h头文件中的标准库函数,因此在使用之前需要包含该头文件。

printf函数的基本语法如下:

#include <stdio.h>

int printf(const char *format, ...); 
  • 1
  • 2
  • 3

其中,format是一个字符串参数,用于指定输出的格式。…表示可变参数列表,用于指定要输出的数据。

printf函数的使用方法如下:

  1. 在程序中包含<stdio.h>头文件。

  2. 在需要使用printf函数的地方,调用该函数并传入相应的参数。

  3. 在format字符串中,可以使用占位符来指定输出的格式,例如:%d表示输出整数,%f表示输出浮点数,%s表示输出字符串等。

以下是一个简单的示例,演示了如何使用printf函数输出不同类型的数据:

#include <stdio.h>

int main() {
    int num = 10;
    float pi = 3.14;
    char str[] = "Hello, World!";
    
    printf("The value of num is: %d\n", num);
    printf("The value of pi is: %f\n", pi);
    printf("The string is: %s\n", str);
    
    return 0; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在上述示例中,我们使用printf函数分别输出了一个整数、一个浮点数和一个字符串。通过在format字符串中使用相应的占位符,我们可以指定输出的格式,并将要输出的数据作为参数传递给printf函数。

运行上述程序,将会输出以下结果:

The value of num is: 10 
The value of pi is: 3.140000 
The string is: Hello, World! 
  • 1
  • 2
  • 3

注意,printf函数中的format字符串中的占位符必须与传递给printf函数的参数类型匹配,否则可能会导致输出结果不正确或程序崩溃。

2.3 scanf()输入函数

scanf函数是C语言中用于从标准输入(键盘)读取格式化输入的函数。它可以根据指定的格式字符串,将输入的数据转换为对应的类型。

scanf函数的基本语法如下: c int scanf(const char *format, ...);

其中,format是一个格式字符串,用于指定输入数据的格式。...表示可变参数,用于接收输入数据的变量。

scanf函数根据格式字符串中的格式说明符来匹配和解析输入的数据。以下是一些常用的格式说明符及其要求:

  • %d:读取一个整数。要求输入的数据必须是一个整数,可以是正数或负数。如果输入的数据不是整数,或者输入的整数超出了int类型的范围,会导致错误或未定义的行为。
  • %f:读取一个浮点数。要求输入的数据必须是一个浮点数,可以是正数或负数,可以包含小数点。如果输入的数据不是浮点数,或者输入的浮点数超出了float类型的范围,会导致错误或未定义的行为。
  • %c:读取一个字符。要求输入的数据必须是一个字符。如果输入的数据长度超过了char类型的范围,只会读取第一个字符。
  • %s:读取一个字符串。要求输入的数据必须是一个字符串,可以包含空格。scanf函数会自动在遇到空格、制表符或换行符时停止读取。如果输入的字符串长度超过了目标字符串的长度,可能会导致缓冲区溢出的问题。
  • %lf:读取一个双精度浮点数。要求输入的数据必须是一个浮点数,可以是正数或负数,可以包含小数点。如果输入的数据不是浮点数,或者输入的浮点数超出了double类型的范围,会导致错误或未定义的行为。
  • %ld:读取一个长整数。要求输入的数据必须是一个长整数,可以是正数或负数。如果输入的数据不是长整数,或者输入的长整数超出了long类型的范围,会导致错误或未定义的行为。

除了以上常用的格式说明符外,scanf函数还支持其他一些格式说明符,如 %u(无符号整数)、%x(十六进制整数)等。

以下是一个简单的示例,演示了如何使用scanf函数读取用户输入的整数:

#include <stdio.h>

int main() {
   int num;
   
   printf("请输入一个整数:");
   scanf("%d", &num);
   
   printf("您输入的整数是:%d\n", num);
   
   return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在上面的示例中,scanf("%d", &num)语句会等待用户输入一个整数,并将其存储在num变量中。然后,通过printf函数将输入的整数打印出来。

需要注意的是,scanf函数在读取输入时,会根据格式字符串中的格式说明符进行匹配和转换。如果输入的数据与格式不匹配,可能会导致错误或未定义的行为。因此,在使用scanf函数时,应该谨慎处理输入的数据类型和格式。

另外,scanf函数在读取输入时,会忽略空白字符(空格、制表符、换行符等)。这意味着,如果在读取字符串时需要包含空格,可以使用%[^\n]格式说明符,表示读取直到遇到换行符为止的字符串

此外,scanf函数还可以通过返回值来判断读取的数据数量。返回值为成功读取的参数数量,如果返回值小于参数数量,可能表示输入格式不匹配或输入结束。

需要注意的是,scanf函数有一些缺点和安全性问题。例如,它无法处理超出变量范围的输入,可能导致缓冲区溢出。为了解决这个问题,可以使用fgets函数读取一行输入,然后使用sscanf函数从字符串中解析数据。

在使用scanf函数时,需要注意以下几点:

  1. 格式字符串中的格式说明符必须与参数列表中的变量类型相匹配,否则会导致错误或未定义的行为。
  2. 在格式字符串中,可以使用空格、制表符和换行符来匹配输入中的空白字符,并且会忽略它们。
  3. 如果输入的数据不符合格式要求,scanf函数会将输入留在输入缓冲区中,下次读取时会继续尝试匹配。可以通过检查scanf函数的返回值来判断是否成功读取数据。
  4. scanf函数在读取字符串时,会根据空格、制表符或换行符来停止读取。如果需要包含空格,可以使用%[^\n]格式说明符,表示读取直到遇到换行符为止的字符串。
  5. scanf函数无法处理超出变量范围的输入,可能导致缓冲区溢出。为了解决这个问题,可以使用fgets函数读取一行输入,然后使用sscanf函数从字符串中解析数据。

总之,scanf函数是C语言中常用的输入函数,可以方便地读取用户输入的数据。但在使用时需要注意输入的数据类型和格式,以及处理可能的错误和安全性问题。


考点1:“普通/占位符”,输入时普通字符必须原样输入。

int a, b;
scanf("%d,%d", &a, &b);
//输入:3,5

scanf("a=%d,b=%d",&a,&b);
scanf("%d",&a);//编码时常写这种
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

考点2:“占位符”,输入时4种输入格式。

int a,b;
scanf("%d%d", &a, &b);
//输入时,中间用空格或tab间隔或回车,最后是回车
//方式一:空格	方式二:tab	方式三:空格tab	方式四 回车
  • 1
  • 2
  • 3
  • 4

考点3:“占位符”,输入时1种输入。

char ch1,ch2;
scanf("%c%c", &ch1, &ch2);
//输入时,只能挨着输入,chr1输入后不能写空格
  • 1
  • 2
  • 3

都是字符就需要连续输入字符

考点4:“占位符”,输入时一种输入。

char ch1, ch2;
int x;
scanf("%c%d%c", &ch1, &x, &ch2);
//输入时,数字前可有空格。如:"a 3b"
//实践:输入时,数字前可有空格或tab或回车。数字后必须直接跟字符。
  • 1
  • 2
  • 3
  • 4
  • 5

考点5、地址列表,输入时&。

int x;
scanf("%d",x);//错误 or 正确
  • 1
  • 2

2.4 字符类型数据的输入输出专属函数

(1)putchar()
格式:putchar(字符常量或字符变量)
功能:输出一个字符但不输出单引号。
如:

putchar('a');	//a
putchar('\n');
putchar(100);	//d
putchar('100');//错误
  • 1
  • 2
  • 3
  • 4

(2)getchar()
功能:接收一个从键盘输入的字符。
注:getchar()没有任何参数,函数的返回值就是输入的字符
如:

char a,b;
a=getchar();
b=getchar();
  • 1
  • 2
  • 3

若输入为:1<回车>
则变量a的值为字符 ‘1’,变量b的值为回车符。

三、选择结构(也叫分支结构)

3.1 两类表达式

构成单个条件、联合多个条件。

(1)关系运算符及关系表达式

<     <=     >     >=     ==     !=

三要素:功能、优先级(6级)、结合性(左->右)
关系表达式的结果是一个逻辑值(0和1,代表真假)

> 4>5 
> 5==5 
> 7=7 
> 1<2<3 
> 5>4>3
  • 1
  • 2
  • 3
  • 4
  • 5

(2)逻辑运算符与逻辑表达式

&&     ||     !

在C语言中,逻辑表达式的值只有 1 和 0 两种值。其中,1表示“真”,0表示“假”。
&&    11级     左 —> 右
||       12级     左 —> 右
!        2级       左 <— 右

//例题:
> 5||5
> 4<3&&9
> 0&&7
> 5>4>3&&3
  • 1
  • 2
  • 3
  • 4
  • 5

做题时要考虑:

  1. 合法
  2. 短路
  3. 优先级,同级结合性

3.2 if语句

格式1:

if(表达式)
	语句序列; 
后续语句;
  • 1
  • 2
  • 3

格式2:

if(表达式)
	语句序列1;
else
    语句序列2;
后续语句;
  • 1
  • 2
  • 3
  • 4
  • 5

格式3:

if(表达式)
	if(表达式)
		语句序列11;
    else
        语句序列12;
else
    语句序列2;
后续语句;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3.3 switch语句

格式:

switch(表达式)
{
    case 常量表达式1	: 语句序列1;
    case 常量表达式2	: 语句序列2;
    ……
    case 常量表达式n	: 语句序列n;
    default		   	: 语句序列n+1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  1. switch后必须用小括号将表达式括起。
  2. case后是常量整型或字符型,不能有变量和逗号运算符。
  3. 各case后常量不能出现相同的值。
  4. case 和 default只是一个入口标号,不起中断作用。它们的顺序可以任意颠倒。switch是按照代码书写顺序执行case,最后执行default。
  5. 执行的过程是:程序执行到switch时,计算小括号内的表达式的值,若该值与某个case后的值相同时,则程序直接跳到该case语句块往后执行;若没有一个case后的值与switch后括号内表达式的值相同,当有default时程序直接跳到default所在语句块执行,当没有default时程序直接跳出switch。switch执行时,先用switch后小括号内的表达式的值对比所有的case后的常量,如果两个值相等,则执行该case语句块,如果该case语句块中有break则直接跳出switch,如果没有break,则继续检查下一个case。
  6. case和default必须出现在switch语句中。
  7. 多个case可以共用一组执行语句
    case 'A':
    case 'B':
    case 'C':printf("CCC\n");break;
    
    • 1
    • 2
    • 3

四、循环结构

1、循环语句
功能:重复执行相同的代码段。
有三格式

格式1——while语句

while(表达式)
{
    //循环体语句序列;
}
//后续语句:
  • 1
  • 2
  • 3
  • 4
  • 5

格式2——do while语句

do
{
    //循环体语句序列
}while(表达式);
//后续语句;
  • 1
  • 2
  • 3
  • 4
  • 5

格式3——for语句

for(i=0; ; i++)
{
    //循环体语句;
    if(i == 35){
        break;
    }
    printf("123");
}
//后续语句
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

exp1一般是定义并初始化循环变量。循环在最初执行一次,只执行一次
exp2一般是是条件判断语句
exp3一般是循环的条件变量的增量(增加或减少)
for执行流程:最初执行一次并只执行一次exp1,然后 exp2->循环体语句->exp3 往复执行。若exp2为假则跳出循环,为真继续循环。
思考:若是没有exp2怎么办?相当于是1,永真,就是死循环。
例题:1+2+3+4+……+100

#include <stdio.h>
int main()
{
    int i,sum=0;

    //while
    // i = 1;
    // while(i<=100){
    //     //sum += i;
    //     sum = sum +i;
    //     i++;
    // }

	// for(i=1; i<=100; i++){
 //        sum += i;	//sum = sum +i;
 //    }
 //    printf("%d\n", sum);

    do{
        sum += i;
        i++;
        
    }while(1);

    //scanf("%d", &i);
    return 0;
    
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

两个控制语句——break continue

格式:break;
功能:中止退出。范围:循环体中和switch体中。

for(i=1; i<4; i++)
{
    i++;
    break;
}
printf("%d", i);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

格式:continue;
功能:结束当前本轮循环直接进行下一轮循环。
范围:循环体中。

for(i=1; i<=10; i++)
{
    //i++;
    if(i==6 || i==5){
        continue; 
    }
    sum += i;
    //.....
}
printf("%d", i);//5
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

如果循环体中的continue前后都有代码,那么如果执行到continue,就相当于只跳过了循环体的后半部分。如下:

for(i=1; i<4; i++)
{
    i++;
    continue;
    i++;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

例题

以下程序的运行结果是:15 or 20 or 25 or 30

#include <stdio.h>

int main()
{
    int i,j,m;
    for(i=5; i>=1; i--)
    {
        m = 0;
        for(j=i; j<=5; j++)
        {
            m = m + i*j;
        }
    }
    printf("%d\n",m);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

以下程序的运行结果是:20 or 21 or 32 or 43

#include <stdio.h>

int main()
{
    int n = 4;
    while(n--);
    printf("%d", --n);
}
以下程序的运行结果是?
#include <stdio.h>

int main()
{
    int i = 5;
    do
    {
        switch(i%2)
        {
            case 0:i--;break;
            case 1:i--;continue;
        }
        i--;
        i--;
        printf("%d", i);
    }while(i>0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

五、数组

1、一维数组
2、二维数组
3、字符串
特点:同一数组中的所有元素都属于同一种数据类型(int char float)数组元素用数组名和相应的下标来确定。
方法:一个数组元素其实就是一个变量,在数组中称为带下标的变量。

5.1 一维数组的定义和引用(只有一个下标)

1、一维数组的定义格式:

类型名 数组名[常量表达式];

如:

float s[25];
float s[5*5];
int a[10];
char s1[30];
  • 1
  • 2
  • 3
  • 4

定义数组有四个注意:

  1. 数组名属于用户定义标识符。要遵守标识符命名规则。比如int for[25];是不行的。
  2. 定义一维数组时数组名后必须用一个方括号(将常量表达式括起来,常量表达式的值表示数组共有多少个元素)。
    如:下面定义是非法的
    int a(10);
    int a{10};
  3. 定义数组时方括号中的表达式不能含有变量。另外表达式的值必须要大于 0 且为正整数。如:
    int a[1.5];
    int a[-10];
    int a[n];
    int a[0];
    int N=9;int a[N];
  4. C语言每个数组在内存中分配空间时是连续分配的。

2、一维数组元素的引用格式:

数组名[下标]
先定义数组后使用
不能整体用数组名
注:C语言中不能对数组进行整体引用,只能单独引用其中的元素。引用时下标可以是如下形式。

char a[10];
a[0] = 'A';
a[3+7] = 'b';//错误
  • 1
  • 2
  • 3

记住:数组元素下标的值在0和size-1之间。即下标范围属于[0, size-1]

3、一维数组的初始化

格式:
类型名 数组名[常量表达式]={初值1, 初值2, ……};
注意:

  1. 定义一维数组时可以将各元素的初始值依次放在一堆{}中赋值给数组(必须用{}将初值括起,且初值之间用一个逗号分隔)。
    int a[5] = {19, 45, 30, 20, 10, 66}; int a[5] = {11};
  2. 多则出错;少则补0。
  3. 定义赋初值的个数大于数组长度,则数组长度可以省略。
    int a[] = {19, 45, 66, 90};//ok
    int a[];
  4. 一个也不给则是随机值。
    int a[5];
  5. 只能在定义数组的同时用{}给数组元素整体赋值。
    a[5] = {19, 45, 30, 20, 10, 66};

5.2 二维数组的定义和引用(有两个下标)

1、二维数组的定义格式:

类型名 数组名[常量表达式1][常量表达式2]
如:
int a[10][10];
char b[3][4];
float s[7][3];
注:

  1. 数组名要遵守用户自定义标识符的命名规则。

  2. 定义二维数组第一个方括号常量表示行数,第二个方括号常量表示列数。

    int a[4*5];	//一维 	20个元素个数
    int a[4][5];//二维 	4行5列 20个元素
    
    • 1
    • 2
  3. 定义数组时方括号中的表达式不能有变量,且表达式的值必须是大于 0 的正整数。

  4. 在内存中二维数组元素的存放也是连续的,先行后列原则。
    在这里插入图片描述

2、二维数组的引用

数组名[行下标][列下标];
注:

  1. 不能整体引用,只能引用其中的元素
  2. 引用二维数组元素时,必须有行下表和列下标两个下标。从0开始依次加1。

在这里插入图片描述

3、二维数组的初始化

方法1:将初值依序放在一对 {} 中,与一维数组初始化格式相同。
例如:int a[2][3] = {1,2,3,4,5,6};
方法二:定义二维数组的同时,按行初始化每一行初值均用一对 {} 括起,采用嵌套的 {} 格式。
例如:int a[2][3]={ {1,2,3}, {4,5,6} };
注意:定义二维数组的同时给数组初始化,则可以省略行数,但列数不能省略。
例如:

int a[][3]= {1,2,3,4,5,6};//对?
int a[2][]= {1,2,3,4,5,6};//错?
int a[][3];//?
  • 1
  • 2
  • 3

5.3 字符数组

1、字符数组的初始化

char s[]={‘a’, ‘b’, ‘c’, ‘d’, ‘e’};
与其它类型数组的初始化方式一样,只是其初始值是字符。

2、字符串

因为字符串最后都有一个字符串结束符(‘\0’),所以用字符数组来存放字符串时一定要有一个元素存放结束符’\0’

  1. 字符串常量后有一个’\0’。
    如:
    "abcd"由5个字符组成:‘a’ ‘b’ ‘c’ ‘d’ ‘\0’
  2. 用字符串给一字符数组进行初始化有三种情况:
    a. 方法一:char a[] = “abcd”;
    b. 方法二:char a[] = {“abcd”};
    c. 方法三:char a[] = {‘a’,‘b’,‘c’,‘d’,‘\0’};
    遇到双引号就加 ‘\0’

3、字符串使用

(1)字符串存储(赋值)四种方法

方法1:利用scanf();键盘输入

char a[10];
scanf(“%s”, a);//输入:ab cd<回车>
scanf(“%s”, &a); //非法
printf(“%s”, a);//输出时,从当前地址开始输出,遇到’\0’结束
注:用 %s 格式输入时,遇到空格符或回车符则自动结束输入。 scanf(“%s,%s”, a,b); 不能正确读取数据。

方法2:利用gets();键盘输入

功能:读取字符串赋值给字符数组。可有空格
注:

  1. 用gets()输入字符时,只有遇到回车才读取字符。

    char str1[10];
    gets(str1);	//输入:ab cd<回车>
    //str:ab cd
    
    • 1
    • 2
    • 3
  2. 不论用%s还是gets()在输入时均不需要输入双引号。若输入了双引号则双引号也作为字符串的有效字符。

puts()输出
功能:屏幕上输出字符数组中的字符。
注:用该函数输出与用%s格式输出一样,只是该函数将’\0’转成’\n’输出。
//如;

puts("abc");put("def");
/* 输出结果:
abc
def
*/
  • 1
  • 2
  • 3
  • 4
  • 5

方法3:利用字符数组初始化

char s[] = "abc";
char s[] = {"abc"};
char s[] = {'a', 'b', 'c', '\0'};
  • 1
  • 2
  • 3

方法4:strcpy()

char a[10];
strcpy(a, "abcd");
  • 1
  • 2

方法4:字符串常量赋值给指针

char *s = "hello";
char *buf = "hello world";
  • 1
  • 2
(2)字符串函数

第 1 函数:strcat()
格式:

#include<string.h>
strcat(字符数组1, 字符数组2);
  • 1
  • 2

功能:“连接”
如:

char a[18] = "jack";
char b[10] = "zhy";
strcat(a,b);
/*
a=>"jackzhy"
b=>"zhy"
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

第 2 个函数:strcpy():
格式:

#include<string.h>
strcpy(字符数组1, 字符数组2);
  • 1
  • 2

功能:将字符数组2中的字符串替换到字符数组1中。函数值为字符串数组1的首地址。
如:

char str1[10] = "123456";
char str2[10] = "abc";
strcpy(str, str2);
// str1=> "abc"
// str2=> "abc"
  • 1
  • 2
  • 3
  • 4
  • 5

第 3 个函数:strcmp()

#include<string.h>
strcmp(字符数组1, 字符数组2);
  • 1
  • 2

功能:函数返回值相同位置不同字符的ASCII码差值。
返回值:一个整型数,是一个插值。

// strcmp("abc", "abc") => 0
// strcmp("abc", "abfc") => 'c' - 'f' => -3
// strcmp("abc", "ab") => 'c' - '\0' => 99
  • 1
  • 2
  • 3

注:不能用关系运算符直接进行字符串大小或字符数组大小的比较。

//这是不行的
"abc" == "abc";
"abc" > "ab";
  • 1
  • 2
  • 3

第4个函数:strlen()
格式:

#include<string.h>
strlen(字符数组);
  • 1
  • 2

功能:求出字符数组的实际长度(不包括结束符)

char a[10] = "4332567";
printf("%d", strlen(a));//7

char a[10] = "abc\018\0";	// \01是8进制
printf("%d", strlen(a));//5

char a[10] = "abc\012\0";	// \012是8进制
printf("%d", strlen(a));//4
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

六、函数

C语言程序的框架有两种:

  1. 一个main() 单框架
  2. 一个main() + 多个子函数 复合框架

注:

  1. 一个源程序文件可由一个或多个函数组成
  2. 一个C程序可以由一个或多个源程序文件组成。
  3. C程序执行从main开始,结束于main结束;可调用其它函数。
  4. 函数不能嵌套定义,但可以互相调用,不能调用main()函数。
    函数的分类:
    (1)无参函数和有参函数
    (2)库函数和用户自定义函数+main()

6.1 函数定义

1、函数定义

函数返回值类型 函数名(形式参数列表)
{
函数体;
}
  • 1
  • 2
  • 3
  • 4

说明:

  1. 函数体可以没有语句,但不能没有花括号,函数名后必须有一对小括号。

  2. 定义有参函数时,形参的定义可以采用传统方式或现代方式两种。

    //传统方式:
    int max(x,y)
    int x,y;	// 不能定义形参以外的其它变量
    {
        //...
    }
    
    //现代方式:
    int max(int x, int y)
    {
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  3. 函数返回值类型:两类

    // 形式1:非void型(int float double等...)
    int fun(int x, int y)
    {
        return 表达式;
    }
    
    // 形式2:void型
    void fun(int x, int y)
    {
        //不加return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

6.2 函数调用的两种方式

方式一:非void型

变量名 = 函数名(实参列表)
  • 1

方式二:void型

函数名(实参列表)
  • 1

6.3 函数使用例题分析

(1)原型声明
方式一:#include<头文件> (库函数)
方式二:声明的格式 (用户自定义函数)
函数类型 函数名(形参类型1 形参1, 形参类型2 形参2, …);
函数类型 函数名(形参类型1, 形参类型2, …);

void sum(int x, int y);	/*函数原型声明*/

int main()
{
    sum(3, 5);
    return 0;
}

void sum(int x, int y)
{
    printf("%d", x+y);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 有头有分号 - 声明
  • 有头有体无分号 - 定义
  • 函数名(实参) - 调用
    (2)函数调用的过程
    在进行函数调用时系统将实参值对应地(按位置次序对应)传给形参,是一种值的单向传递。
    实参与形参之间有一个关系。(顺序、类型、个数 一致)

七、在函数中的变量

7.1函数的定义与使用

函数定义:

函数返回值类型 函数名(形式参数列表)
{
    函数体;
}
//这是函数的定义
  • 1
  • 2
  • 3
  • 4
  • 5

函数声明:

函数类型  函数名(形参类型1  形参1, 形参类型2  形参2,  ...);
函数类型  函数名(形参类型1, 形参类型2,  ...);
//这是函数的声明语句
  • 1
  • 2
  • 3
void sum(int x, int y);

int main()
{
    sum(3, 5);
    return 0;
}

void sum(int x, int y)
{
    printf("%d", x+y);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

7.2 变量的三个属性

变量三属性:

  1. 类型(用类型去定义变量)
  2. 作用范围(根据位置)
    a. 内部(局部)变量
    b. 外部(全局)变量
  3. 存储类别
    a. auto
    b. static
    c. register
    d. extern

1、变量类型

就是数据类型,如int、float、double

2、作用范围

(1)局部变量(内部变量)
定义:在一个函数内部定义的变量称为局部变量

  1. 局部变量只在函数(它所在的函数)内部有效
  2. 不同函数中可以出现同名变量,他们分别是不同函数的局部变量
  3. 复合语句中定义的变量只能在此复合语句中有效
  4. 所有形式参数是局部变量

(2)全局变量(外部变量)
定义:在函数外部定义的变量为全局变量

  1. 全局变量的有效范围是在本文件内从定义该变量的位置开始到本文件结束。
  2. 全局变量可以在它的有效范围内被每个函数引用(使用)。
  3. 在同一文件中若全局变量与局部变量同名,局部变量“覆盖”全局变量。

全局变量也是静态变量

3、存储类别

(1)auto变量(默认)

  1. auto类别变量用完后释放所占用空间。
  2. 局部变量默认为auto类别,无初始化时,初始值为随机值。
  3. 使用时间短,一般都为auto类别变量。

(2)static变量

  1. static从定义到程序运行结束均占用存储空间。
  2. 全局变量默认为static类别,无初始化时,初值为0。
  3. static类别变量只能进行一次初始化。

(3)register类别变量

  1. register类别只能是局部变量才能被说明。
  2. 一般不能直接使用。

(4)extern类别变量

  1. extern类别变量可以加大变量的作用范围。
  2. 两种说明格式
    1. 格式 1:
      定义的同时说明类别:
      存储类别 类型名 变量名;
    2. 格式 2:
      分别定义、说明:
      类型名 变量名;
      存储类别 变量名;

八、预编译命令

8.1 预编译命令

文件包含(include)
#include <>
#inclulde “”

8.2 宏

1、第一种:无参宏定义

格式:
#define 宏名 宏内容
功能:用一个指定的标识符(宏名)来代表一串字符串(宏内容)。
如:
#define PI 3.141592
#define N 10

注:

  1. 宏名我们一般用大写字母表示,遵守用户自定义标识符命名规则。
    如:#define PI “abcdefg”
  2. #define 可以在函数外定义也可以在函数内定义,但该命令应该在单独一行上。
  3. 在进行宏定义时,可以引用已经定义的宏名进行层层置换。
  4. 在进行宏替换时,必须先替换完所有的宏后再运算,同时替换过程中不能乱加括号。

2、第二种:带参宏定义

#define 宏名(参数列表) 宏内容
功能:提供了一种更加灵活的替换方式。
如:#define s(x,y) x*y+2

注:

  1. 在定义有参宏时,参数列表必须用一对小括号括起且小括号和宏名之间不能有空格。
  2. 对有参宏名进行替换时,需要将形参改成相应的实参,并且注意分清形参和实参的对
    应关系。

九、指针

9.1 指针变量定义

C语言有两种变量:其中变量(普通变量)存储内容值;地址变量(指针变量)存储地址值。

1、定义的格式:

类型名 *指针变量名;

如:

int a, b, *p1, *p2;
float x, y, *p3, *p4;
char s, *p5, *p6;

int b, *a;
int* a;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

注:

  1. 定义变量(普通变量、指针变量)都必须在前面有类型名。
  2. 在定义指针变量时,指针变量名前的“*”表示现在定义的是一个指针类型的变量。星号并不是指针变量名的一部分,只是一个标志。
  3. 指针变量专门用来存放地址,禁止将一个整型值直接赋值给一个指针变量。

2、指针变量的引用

“&”取地址运算符,通过&运算符可以取出普通变量的地址。
指针运算符,*可以取出指针变量所指向的普通变量的值,(间接引用普通量)。
*指针变量名

如:

int a,b=20,c=30,d=40,*p;
p = &d;
a = *p;	//a=d;
  • 1
  • 2
  • 3
  1. 可以通过赋值使一个指针变量“指向”某一普通变量(指针变量 = &普通变量)
  2. 在C语言中正确的做法是先让指针变量指向一个确定的存储单元后,再通过该指针变量引用它所指向的存储单元。
  3. 变量名(普通变量、指针变量)都表示其存储单元内的值:
    p1=p2; /* p1指向了p2所指向的单元 */
  4. 若指针变量p指向变量a,即将变量a的地址赋给了指针变量p。
  5. 所有的指针变量在内存中分配的字节数相同。sizeof()

说明:int fun(int a[10]) <=> int fun(int *a) <=> int fun(int a[])
若数组做为形参,则将数组名做指针变量来处理。

9.2 指向数组的指针变量

1、指向数组元素的指针变量

由于数组元素与普通一样,所以定义指向数组元素的指针变量与定义指向普通变量的指针变量完全一样
在这里插入图片描述

2、指向一维数组的指针变量

注:
(1)C语言中规定:数组名代表数组的首地址,而且是一个地址常量

在这里插入图片描述

(2)当指针变量指向数组中的某一个元素时,指针变量加1后指向数组中后一个元素,指针变量减1时指向数组中前一个元素。

在这里插入图片描述

举例指针在数组中的使用:
在这里插入图片描述

(3)若两个指针变量指向同一个数组,则这两个指针变量可以进行大小比较如:

char s[10];
char *p1 = s+3, *p2 = &s[7];
//则 p1 > p2  ===> 0	p1 < p2 ===> 1
// p1 - p2 == -4
  • 1
  • 2
  • 3
  • 4

(4)在形参中的数组实际上是一个指针变量,并不是真正的数组,因为该“数组名”的值是可以改变的,而真正的数组名的值是不能改变的。
(5)若形参是数组或指针变量,则在函数中可以通过该形参改变实参的值。

9.3 指向多维数组的指针变量

若a是一个二维数组,则:

  1. a+i 是行指针,即指向的是一整行。若对它加1则指向下一行。
  2. *(a+i)和a[i]一样,都是一个列指针,即指向一个元素。
  3. *(a+i)+j 和 a[i]+j一样,都表示元素a[i][j]的地址。即与 &a[i][j]等价。
  4. ((a+i)+j)、(a[i]+j)、((a+i))[j] 和 a[i][j] 一样,都表示元素。
//若有以下定义:
int w[2][3];
//则对w数组元素的非法引用是:
/*
A:*(w[0]+2)
B:*(w+1)[2]
C:w[0][0]
D:*(w[1]+2)
E:w[1]+2
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

1、指向多维数组元素的指针变量

如:指向二维数组的指针变量。

int a[3][4];
int *p = &a[0][3];
//则:
p+1指向元素a[1][0];
p+4指向元素a[1][3];
p-2指向元素a[0][1];
常用于取二维数组a元素地址的方式:&a[i][j]、a[i][j]*(a+i)+j 元素地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在这里插入图片描述

利用列指针遍历二维数组:
在这里插入图片描述

2、指向由m个元素组成的一维数组的指针变量

定义指向由m个元素组成的一维数组的指针变量的格式:
基类型 (*指针变量名)[m];
如:

int a[5][7];//a是行地址
int (*p)[7];
p=a;
char b[10][80];
char(*r)[80];
r = b+5;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

利用数组指针遍历二维数组:
在这里插入图片描述
等价的另一种利用行指针移动的方式遍历:
在这里插入图片描述

在这里插入图片描述
用画图方式更好理解程序中的数组指针,看看下列程序的结果?

#include<stdio.h>
int main()
{
	int a[][3] = {{1,2,3},{4,5,0}}, (*pa)[3];
	int i;
	pa = a;
	for(i=0; i<3; i++)
		if(i<2)
			pa[1][i] = pa[1][i] - 1;
		else
			pa[1][i] = 1;
	printf("%d\n", a[0][1]+a[1][1]+a[1][2]);//2+4+1
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

7.4 指向字符串指针的指针变量

符串常量:C语言对字符串常量是按首地址处理字符串常量的

  1. char str[] = “China”
  2. char *p = “China”
  3. str = “Chinese” 错误
  4. p = “Chinese”

7.5 指向函数的指针变量

函数名与数组名一样,是起始地址,而且是一个地址常量
定义指向函数的指针变量的方式:
类型名 (*指针变量名)();
有()的标志说明是函数

一维数组 列地址
二维数组 行地址
函数 函数地址

区分:

/*
函数指针:类型名 (*指针变量名)();
普通变量:类型名 普通变量名:
普通数组:类型名 数组名[];
普通指针:类型名 *指针变量名:
函数定义:类型名 函数名()
{....}
数组指针:类型名 (*指针变量名)[M];
*/

//------------------------------------------------------------------
// example:
int min(int a, int b)
{
	return a > b ? b : a;
}

int max(int a, int b)
{
	return a > b ? a : b;
}

int main()
{
	int x=6, y=10;
	// 定义函数指针变量
	int (*p)();
	p=max;
	printf("%d", max(x,y));
	printf("%d", p(x,y));
	p=min;
	printf("%d", min(x,y));
	printf("%d", p(x,y));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

注:
(1)在定义指向函数的指针变量时,要注意有两个小括号必须要有,不需要定义形参。
(2)单独的函数名代表该函数的首地址(函数的入口地址)
(3)函数的指针变量只能指向函数的入口处(函数的首地址),不能指向函数中的某条指令。(另外,对指向函数的指针变量加1是没有意义的)。
(4)给指向函数的指针变量赋值时,只写函数名即可,不必写参数。

7.6 返回指针的参数

类型名 函数名(形参)
{
    
}

返回指针的函数的定义方式为:
类型名 *函数名(形参)
{
    return 地址值;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
int *fun(int *x,int *y)
{
	if(*x < *y)
		return x;
	else
		return y;
}

int main()
{
	int a=7, b=8, *p, *q, *r;
	p=&a;
	q=&b;
	r=fun(p,q);
	printf("%d,%d,%d\n", *p, *q, *r);//7 8 7
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这里插入图片描述

7.7 指针数组和指向指针的指针变量

1、若一个数组的所有元素均为指针类型(地址),则称为指针数组。

格式:
类型名 *数组名[常量表达式];
int *s[10];

注:
(1)要注意它与定义指向m个元素组成的一维数组的指针变量之间的区别
类型名 *数组名[常量表达式];
类型名 (*指针名)[常量表达式m];
(2)它的每个元素都是一个指针类型(地址),即它的每个元素相当于一个指针变量。

int *a[10]:
int a[10]:
int a[10][10]:
  • 1
  • 2
  • 3

在这里插入图片描述
2、指向指针的指针变量
用来存放指针变量地址的指针变量 称为 指向指针的指针变量。
定义格式:基类型名 **指针变量名;
在这里插入图片描述

如:

int a=3;
int *p = &a;
int **k = &p;
/*
则:*k得到变量p(变量a的地址)
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

7.8 空指针

指针变量可以有空值,即指针变量不指向任何变量,不指向任何有用的存储单元。

在系统中已将NULL定义为0,即NULL的值为0。
int a,b,c,*p=NULL;
此时p的值为空指针,即p不指向任何有用的存储单元。尽管NULL的值为0,但我们不能认为p指向了地址为0的存储单元。

注:
(1)当一个指针变量的值为空指针时,我们不能引用它所指向的存储单元。
(2)若某指针(地址)的基类型为void型,则有引用时应进行相应的强制类型转换。
在这里插入图片描述

#include <stdio.h>
main()
{
    int a[]={1,2,3,4,5,6,7,8,9,10,11,12};
	int *p=a+5,*q=NULL;
	*q=*(p+5)
	printf("%d %d\n",*p,*q);
}
输出:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

九、构造类型

9.1 结构体类型

1、构造结构体类型

struct 结构体类型名
{
    成员1的定义;
    成员2的定义;
    ......;
    成员n的定义;
};

struct student
{
    int sn;
    int age;
    char sex;
    int s[3];
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

注:
(1)定义成员的方式与定义普通变量的方式一样
(2)成员列表必须用一对花括号括起
(3)结构体名可以省略。

2、定义结构体变量

A.先定义结构体类型名再定义结构体变量

main()
{
    struct student
    {
        int sn;
        int age;
        char sex;
        int s[3];
    }; /*类型定义*/
    
    struct student stu1,stu2,st[25];/*变量定义*/
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

B.在定义结构体类型同时定义变量

struct student
{
    int sn;
    int age;
    char sex;
    int s[3];
}stu1,stu2,st[25]; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

C.类型、变量同时定义,类型名student省略

struct
{
    int sn;
    int age,
    char sex;
    int s[3];
}stu1,stu2,st[25]; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

注:
(1)结构体变量在内存中占用字节数为各成员占用字节数总和。【有待商榷】
在这里插入图片描述

3、结构体变量的初始化及引用】】使用结构体变量、结构体数组、结构体指针变量

(1)再定义结构体变量的同时可以将各成员的初值按顺序放在一对花括号中,来进行对结构体变量的初始化。若初值个数多于成员个数则出错,若初值个数少于成员个数,则多余成员自动赋0。

struct aa
{
    int a;
    char b[10];
    float c;
} a1={30, "china", 40.5}, a2={60,"hello"};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

(2)结构体变量不能整体引用,只能引用它的成员(同数组相似)

/* 引用结构体成员的方式:
结构体变量名.成员名
其中.为成员运算符
*/
printf("al=%d,%s,%f", a1);//非法/
printf("al=%d,%s,%f", a1.a, a1.b, a1.c);
a1.a=80;
a1.b="xinjiang";//strcpy(a1.b, "abc")
a1.c=60.5;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

(3)结构体指针变量
在这里插入图片描述
(4)指向结构体数组的指针

struct student
{
    int num;
    char name[20];
    char sex;
    float score;
};
struct student stu[3] = {{1001,"zhang",'M',60.5},
                         {1002,"li",'M',100},
                         {1003,"wang",'W',90.9}};
struct student *p=stu;// &stu[0]等价

//引用成员:
stu[1].num;(p+1)->num;(*(p+1)).num;
stu[0].name;p->name;(*p).name;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述
注:
(1)可以用结构体变量的成员作为实参,它与普通变量作为实参的用法是一样的。
(2)用结构体变量作为实参时,要求形参必须是同一结构体类型的变量,传递后形参与实参各对应成员值是一样的。
(3)也可以用结构体类型的地址(指针变量或数组)作为实参,要求形参必须是同一结构体类型的指针变量或数组。只要是地址传递,则间可以通过形参来改变实参的值。

9.2 共用体类型

共用体中的所有成员共用同一段内存(所有成员的起始地址都是一样的)
格式:

union 共用体名
{
    成员列表;
};
  • 1
  • 2
  • 3
  • 4

注:
(1)成员列表为定义该共用体的成员,成员定义的方式与普通变量的方式一样。
(2)成员列表必须用一对花括号括起。
(3)共用体名可以省略。

1、共用体变量的定义

(1)先定义类型,再定义变量
(2)定义类型的同时,定义变量
(3)直接定义变量

union data
{
    int i;
    char ch[10];
    float s;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

注:
由于共用体类型变量的所有成员公用同一段内存,所以共用体类型变量所占的字节数等于该共用体类型中占字节数最多的成员所占的字节数。

2、共用体变量的引用

注:
(1)不能整体引用共用体变量,只能引用其成员。
(2)同类型成员共享值。
(3)在内存中整型数据的二进制数低8位占用前面一个字节,高8位占用后面一个字节。
如:
整数255,在内存中的存储形式是:
1111 1111 0000 0000
一个字符型数据占用一个字节,对于数组来说前面元素占用前面的字节。
(4)共用体变量之间可以相互赋值,赋值后两个变量应使用同一成员。
(5)共用体变量的地址与各成员的地址都相同的。
(6)在定义共用体时,可以对其进行初始化,但只能有一个初值且必须用花括号将初值括起。相当于给第一个成员赋值。
(7)共用体、结构体的成员均可以是共用体或结构体类型
(8)不能用共用体类型变量做为函数参数。
(9)计算共用体占用字节数。

在这里插入图片描述

9.3 typedef

用typedef 定义新类型名
在编程中可以用typedef来定义新的类型名来代替已有的类型名。给已有类型起别名。
格式:
typedef 已有类型名 新的类型名;

如:

typedef int INTEGER;
//以后在定义变量时int和INTEGER是等价的。
INTEGER a[10],b; int a[10],b;
  • 1
  • 2
  • 3

(1)typedef可用于定义各种类型名,但不能定义变量,即只要见到typedef则该语句最后的标识符必定是一个类型名而不是变量名。
(2)typedef只能对已经存在的类型新增一个别名,而不是创造新类型。即在ypedef后必须是一个已有的类型。
在这里插入图片描述

十、位运算与文件

10.1 位运算

位运算的操作对象只能是整型或字符型数据。
C语言提供6种位运算符:
&   |   ^   ~   <<   >>
复合赋值运算符:
&=   |=   ^=   <<=   >>=

1、按位与运算(&)

两个相应的二进制位都是1时,它们按位运算后的结果才是1,否则为0。
1&1=1
1&0=0
0&1=0
0&0=0
全1为1;有0为0。
作用:清零。

2、按位或运算(|)

两个相应的二进制位中只要有一个为1,则它们按位或运算后结果为1。
1|1=1
1|0=1
0|1=1
0|0=0
作用:将特定位 置1。

3、按位异或运算(^)

当两个相应的位同为1或同为0时,按位异或运算结果为0;两个相应位一个为1另一个为0时,按位异或运算结果为1。
1^1=0
1^0=1
0^1=1
0^0=0
相同为0,相异为1

4、按位取反运算(~)

按位取反运算符是一个单目运算符。按位取反后0变1,1变0。
注:
对一个数按位取反得到的值为该数+1后再乘-1。???

5、按位左移运算符(<<)

格式:数<<n
功能:将二进制位按位依序左移n位。
对一个十进制数左移n位后得到的值为该数乘以2n的积。

6、按位右移运算符(>>)

格式:数>>n
功能:将二进制位按位依序右移n位。
若该数为一个负数并且不能被2n整除则得到的数为商加-1。

10.2 文件

1、文件概念

  1. 文件:记录在外部存储介质(外存)上的数据的集合。
  2. 文件的分类:
    • 文本文件(ASCII码文件):每个字符存储在文件中。
    • 二进制文件:以二进制存储。
    • C语言对文件的存取是以字符(字节)为单位的。
  3. 文件类型指针
    考试中,记住FILE就是文件类型名,它是一个结构体类型。对一个文件进行操作,通过文件指针进行的,定义如下:FILE *fp, *in, *out;

2、文件的打开与关闭

1、文件的打开(fopen()函数)
格式:fopen(文件名,文件使用方式)
功能:按指定的“使用方式”打开文件,函数返回打开文件的指针,该指针的基类型为文件类型。文件名和文件使用方式均为字符串。
如:以只读方式打开文件data.txt,并用指针变量fp指向它。

FILE *fp;
fp = fopen("data.txt", "r");
  • 1
  • 2

打开文件的“使用方式”:
“r”:打开已存在的文件
“w”:刷新写、创建写
“a”:追加写、创建写
“+”:增强
“r+” “w+” “a+”
“rb” “wb” “ab”
“rb+” “wb+” “ab+”

注:
(1)文件使用方式只能用小写字母,文件名用大写或小写均一样。
如:

FILE *fp;
fp = fopen("c:\\tc\\data.txt", "w");
  • 1
  • 2

(2)在“文件使用方式”中若含有字母 b,则打开的是一个二进制文件(bit)。
(3)当fopen“打开”失败时,函数返回NULL。

if (fp = fopen(文件名, 文件使用方式)==NULL)
{
    printf("can not open this file\n");
    exit(0);
}
  • 1
  • 2
  • 3
  • 4
  • 5

2、文件的关闭(fclose函数)
文件使用完后应该关闭该文件。
格式:fclose(文件指针);
如:fclose(fp);

3、文件的读写

(1)fputc()
格式:fputc()
功能:把一个字符写到文件指针所指的文件中。其中字符可以是字符常量也可以是字符变量。若输出成功则函数返回输出的字符,失败则返回EOF(stdio.h中定义为-1)。

(2)fgetc()
格式:fgetc(文件指针)
功能:从文件指针所指文件中读取一个字符。若读取成功则函数返回读取的字符,失败(遇到文件结束)则返回EOF。

char ch;
ch = fgetc(fp);
  • 1
  • 2

(3)fgets()
格式:fgets(str, n, fp)
功能:其中str表示一个字符指针,可以是字符数组名也可以是字符指针变量名。从fp所指文件中读取n-1个字符(不是n个字符),并在这些字符最后加一个字符串结束符’\0’后赋给str。
函数返回str的首地址。

(4)fputs()
格式:fputs(str, fp)
功能:向fp所指文件中写(输出)str中的字符串,str可以是字符串常量、字符数组或字符指针变量。在输出时字符串的结束符’\0’不会输出。若输出成功则返回0,失败返回EOF(stdio.h中定义为-1)。

(5)fread()、fwrite()
格式:
fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);
其中:
buffer时数据的地址
size是每次读写的字节数
count表示让函数进行多少次的读写
fp是要进行读写的文件指针变量
功能:用来读写一个连续的数据块
注:

  1. 这两个函数以二进制方式进行读写。

(6)fprintf()、fscanf()
格式:
fprintf(文件指针, 格式说明符, 输出列表);
fscanf(文件指针, 格式说明符, 输入列表);
功能:按格式说明符所指定的格式向文件中读写(输入输出)数据。其中格式说明符和输入(输出)列表的用法与scanf和printf函数相同。

补充:feof(文件指针)
作用是测试文件的当前读写位置是否到达文件末尾,若是则返回非0值(真),否则返回0(假)。

while(!feof(fp))
{
    
}
  • 1
  • 2
  • 3
  • 4

4、文件当前位置读写函数

(1)重新定位指针
格式:rewind(文件指针)
作用:使当前的读写位置重新指向文件的开头。函数无返回值。

(2)fseek()
格式:fseek(文件指针,位移量,起始点)
功能:将当前的读写位置从“起始点”开始按“位置量”所指定的移动字节数向后移动。
起始点有:
SEEK_SET 或 0 (表示“文件的开始”)
SEEK_CUR 或 1 (表示“当前位置”)
SEEK_END 或 2 (表示文件末尾)
位移量:要在数值后加字母l或L。
如:
fseek(fp, 100L, SEEK_SET)

例题分析:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
(3)ftell()
格式:ftell(文件指针)
功能:返回当前文件的位置值,用相对于文件头的位移量表示。若返回-1L表示出错。
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/784082
推荐阅读
相关标签
  

闽ICP备14008679号