C语言总结:C语言基础

1、自动转换发生在不同数据类型运算时,在编译的时候自动完成。
2、转换后不会改变原数据的类型及变量值,只在本次运算中临时性转换。
3、强制转换后的运算结果不遵循四舍五入原则。
4、而10.0%3则是错误的;运算后的符号取决于被模数的符号,如(-1)%3 = -1;而10%(-3) = 1。
5、注意:复合运算符中运算符和等号之间是不存在空格的。
6、使用do-while结构语句时,while括号后必须有分号。

do{
//执行代码块
}while(表达式);  //注意这里有分号

7、在不知道循环次数的情况下适合使用while或者do-while循环:
如果有可能一次都不循环应考虑使用while循环
如果至少循环一次应考虑使用do-while循环。
8、在没有循环结构的情况下,break不能用在单独的if-else语句中;
在多层循环中,一个break语句只跳出当前循环。
9、break是跳出当前整个循环,continue是结束本次循环开始下一次循环。
10、

switch(表达式){
	case 常量表达式1:执行代码块1 break;
	...
	case 常量表达式n:执行代码块n break;
	default:执行代码块n+1;
}

switch后面的表达式语句只能是整型或者字符类型。
各case和default子句的先后顺序可以变动,而不会影响程序执行结果。
default子句可以省略不用。
11、形参与实参
形参只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。
因此,形参只有在函数内部有效。
注意:函数调用结束返回主调函数后则不能再使用该形参变量。
实参可以是常量、变量、表达式、函数等。
无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值等办法使实参获得确定值。
12、C语言根据变量的生存周期来划分,可以分为静态存储方式和动态存储方式。
静态存储方式:是指在程序运行期间分配固定的存储空间的方式。静态存储区中存放了在整个程序执行过程中都存在的变量,如全局变量
动态存储方式:是指在程序运行期间根据需要进行动态的分配存储空间的方式。动态存储区中存放的变量是根据程序运行的需要而建立和释放的,通常包括:函数形式参数;自动变量;函数调用时的现场保护和返回地址等
13、C语言中存储类别又分为四类:
自动(auto)(可以省略),动态存储方式、
**静态(static)**用static修饰的为静态变量,如果定义在函数内部的,称之为静态局部变量;如果定义在函数外部,称之为静态外部变量、
寄存器的(register)
外部的(extern)

静态局部变量属于静态存储类别,保存在静态存储区的静态存储单元,在程序整个运行期间都不释放;
静态局部变量在编译时赋初值,且只赋初值一次
如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)空字符(对字符变量)

为了提高效率,C语言允许将局部变量得值放在CPU中的寄存器中,这种变量叫“寄存器变量”,用关键字register作声明。

void fn(){
	register int i;  //定义i为寄存器类型变量
}

注意:只有局部自动变量形参可以作为寄存器变量
一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量;
局部静态变量不能定义为寄存器变量

用extern声明的的变量是外部变量,外部变量的意义是某函数可以调用在该函数之后定义的变量

#include <stdio.h>

int main()
{
   extern int x;  //使用extern可以使用函数之后的变量
   printf("extern x=%d\n",x);
   
   return 0;
}
int x=100;

输出为extern x=100
如果没有extern int x,则会报错

14、内部函数(静态函数)与外部函数(默认)
static [数据类型] 函数名([参数])
这里的static是对函数的作用范围的一个限定,static限定该函数只能在其所处的源文件中使用,因此在不同文件中出现相同的函数名称的内部函数是没有问题的

在C语言中能被其他源文件调用的函数称谓外部函数 ,外部函数由extern关键字来定义,形式为:
extern [数据类型] 函数名([参数]) //可默认不写extern

外部函数练习
hello.c

#include <stdio.h>
#include "test.c"   //引用test.c文件,可以使用test.c文件的静态函数
extern void printLine();    
{
   printf("**************\n");   
}
int main()
{
    say();  //这里调用test.c中的say()
    return 0;
}

test.c

#include <stdio.h>
void printLine(); //先进行函数的声明,调用了hello.c文件的printLine方法
static void say()  //设置say()为静态函数,外部文件函数不能直接使用,可以导包使用
{  
printLine();  //然后使用方法
printf("I love imooc\n");
printf("good good study!\n");
printf("day day up!\n");
printLine();
}

输出结果:

**************
I love imooc
good good study!
day day up!
**************

15、在C中,没有字符串类型,使用字符数组表示字符串
16、在实际工作中,C程序通常运行在linux/unix操作系统下
17、float单精度 6位小数,double双精度 15位小数
18、数组不是基本数据类型,而是构造类型
19、C99标准提供了_Bool型(此时为整数类型,只有0、1赋值,提供<stdbool.h>定义bool代表_Bool,true代表1,false代表0
20、墙砖符号只针对最近的操作数有效,往往使用小括号提升优先级
21、默认传递值的类型基本数据类型、结构体、共用体、枚举类型
默认传递地址的类型指针、数组
22、定义常量的两种方式,#define预处理器 const关键字
#define 常量名 常量值
const 数据类型 常量名 = 常量值;
const 在编译和运行时起作用,define在编译的预处理阶段起作用
23、俩种类型的头文件:程序员编写的头文件、C标准库自带的头文件
头文件包含:常量、宏、系统全局变量、函数原型等等
头文件在代码层面有防止重复引入的机制。
头文件只能包含变量和函数的声明,不能包含定义
24、变量的作用域
全局变量:保存在全局区,作用域默认为整个程序,即.c和.h文件
局部变量:保存在栈中,函数被调用时才动态的为变量分配存储单元,作用域为函数内部
同一个作用域变量名不能重复,不同的作用域可以
由{}包围的代码块也拥有独立的作用域
25、
静态局部变量存储于进程的静态存储区(全局性质),只会被初始一次,即使函数返回,值也会保持不变
静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量
适用于定义不需要与其他文件共享的全局变量,能够有效的降低程序模块之间的耦合,避免不同文件同名变量的冲突,不会造成误用。
26、静态函数只能在声明它的文件中可见,其他文件不能引用该函数,不同文件可以使用相同名字的静态函数
27、预处理命令
以#开头的命令

#if _WIN32 
#include <windows.h>
#elif __linux__ 
#include <unistd.h>
#endif
int main() {
//不同的平台下调用不同的函数
#if _WIN32 //识别windows平台
Sleep(5000);
#elif __linux__ //识别linux平台
sleep(5);
#endif
puts("hello~");
getchar();
return 0; }

28、C语言宏定义 #define N 100
29、枚举:一种构造数据类型

#include <stdio.h>
void main() {
	enum DAY
	{
		MON = 1, TUE = 2, WED = 3, THU = 4, FRI = 5, SAT = 6, SUN = 7
	};
	enum DAY day;
	day = WED;
	printf("%d", day); //3
	getchar();
	return 0;
}
enum {
	MON = 1, TUE = 2, WED = 3, THU = 4, FRI = 5, SAT = 6, SUN = 7
}day; //该枚举类型只能使用一次

30、头文件的工作原理

31、函数调用规则
1)当调用(执行)一个函数时,就会开辟一个独立的空间(栈)
2)每个栈空间是相互独立的
3)当函数执行完毕后,或者执行到return,会返回到函数调用位置,继续执行
4)如果函数有返回值,将函数值赋给接受的变量,返回值类型要明确,要求返回值和返回类型要匹配,或者可以相互转换(自动或者强制)。

32、&获取地址,如&n,获取变量n的地址;
int* p,声明int型的指针p,
取值符,如p,获取p指针指向的变量的值

33、C语言不支持函数重载,支持可变参数函数

34、局部变量必须初始化

#include <stdio.h>
void main() {
	int i;  //这里未初始化,直接报错
	printf("%d", i);
}

全局变量,系统会自动进行初始化

int i = 0;
char c = '\0';
float f = 0.0;
double d = 0.0;

35、static关键字
静态局部变量(由static修饰的局部变量),如果未赋值,编译器也会把它初始化,且只初始化一次
普通变量每次执行都会初始化

void fn_static(){
	static int n = 10;
	printf("static n=%d\n",n); //10
	n++; //11
	printf("n++=%d",n); //11
}

fn_static(); //10 11
fn_static(); //11 12

36、在一个文件中,使用另外一个文件的全局变量,使用extern引入即可,
静态全局变量,只能在本文件中使用,不能在其他文件中使用
file1.c

int num1 = 10;//普通全局变量
static int num2 = 20;//静态全局变量,只能在本文件中使用,不能在其他文件中使用

file2.c

#include <stdio.h>

//在一个文件中,使用另外一个文件的全局变量,使用extern引入即可
extern int num1;
//extern int num2;
int num2 = 30;
void main() {
	printf("num1=%d num2=%d", num1, num2);
}

37、非静态函数可以在另一个文件中通过extern引用,不同的文件可以使用同名的静态函数
38、带参宏定义和函数的区别
宏展开仅仅是字符串的替换,不会对表达式进行计算;宏在编译之前就被处理掉了(预处理阶段),它没有机会参与编译,也不会占用内存。
函数是一段可以重复使用的代码,会被编译,会被分配内存,每次函数调用,就是执行内存中的代码。

#include <stdlib.h>
#include <stdio.h>

#define D(x) (x)*(x)

int SQ(int y) {
	return y * y;
}

int main() {
	int i = 1;
	while (i <= 5) {
		printf("%d^2=%d\n", i-1, SQ(i++)); //printf对参数是从右往左计算,从左往右输出
	}

	int j = 1;
	printf("------------\n");
	while (j <= 5) {
		printf("%d^2=%d\n", j-2, D(j++)); 
		//这里的D表示的宏的具体为(j++)*(j++)
	}

	system("pause");
	return 0;
}

39、
1)预处理功能是C语言特有的功能
2)宏是预处理的一种,用#define来定义,宏调用时以实参代替形参,而不是值传递。
3)文件包含是预处理的一个重要功能,它可以把多个源文件连接成一个源文件进行编译,结果将生成一个目标文件。
4)条件编译允许只编译程序中满足条件的程序段,使生成的目标程序较短,从而减少内存的开销并提高程序的效率。
5)使用预处理功能便于程序的修改、阅读、移植和测试,也便于实现模块化程序设计。

40、数组是构造类型,以地址传递、传递指针的方式,因此当把一个数组传递给一个函数或者变量时,函数/变量操作数组会影响到原数组
double arr[6];
arrLen=sizeof(arr)/ sizeof(double);
数组名就代表该数组的首地址,即arr[0]地址

41、C语言没有专门的字符串变量,没有string类型,通常用一个字符串数组来存放一个字符串。

42、字符串使用null字符(‘0’)终止的一维字符数组,一个以null结尾的字符串,包含了组成字符串的字符。
结论:如果在给某个字符数组赋值时,(1)赋给的元素的个数小于该数组的长度,则会自动在后面加’\0’表示字符串结束,(2)赋给的元素的个数等于该数组的长度,则不会自动添加’\0’

43、对字符数组只能对各个元素赋值,不能用以下方法对字符数组赋值

char str[10];
str="hello world!"//错误
str[0]='y'//ok

对字符指针变量,采用下面方法赋值可以

char* a="yes";
a="hello world!";//ok

即如果定义了一个字符数组,那么它有确定的内存地址(即字符数组名是一个常量);而定义一个字符指针变量时,它并未指向某个确定的字符数据,并且可以多次赋值

常用字符串函数
在这里插入图片描述
44、
1)程序中往往依靠检测’\0’的位置来判断字符串是否结束,而不是根据数组的长度来决定字符串长度。因此,字符串长度不会统计’\0’,字符数组长度会统计
char str1[]=“dssda”;//默认后面加’\0’
char str2[]={‘C’,‘H’,‘I’,‘N’,‘A’};//字符数组后面不会加’\0’,可能会有乱码
2)保证数组长度始终大于字符串实际长度
3)系统对字符串常量也自动加一个’\0’作为结束符。例如’‘ggg"共有3个字符,但在内存中占4个字节,最后一个字节’\0’是系统自动加上的(可以通过sizeof()函数验证)。
4)系统会默认将字符数组的剩余元素空间,全部设置为’\0’,比如char str[6]=“ab”,str内存布局就是[a][b][\0][\0][\0][\0]

45、指针:内存的地址;指针变量:保存了内存地址的变量;获取变量的地址用&;
指针类型:int* ptr = &num;ptr就是指向int类型的指针变量,即ptr是int* 类型;
获取指针类型所指向的值,使用:(取值符号),比如:int * ptr,使用ptr获取ptr指向的值。
即,&获取变量的地址,*ptr获取指针类型变量所保存的地址的数值

46、指针变量声明
int a=1;
int *b=&a;
如果要输出一个变量的地址,使用格式是%p
printf(“b的地址值为%p\n,b存放的值是一个地址为%p\n,b指向的值为%d”,&b,b,*b);
//0x00AFFEC0
//0x00AFFECC
//1

47、指针递增

#include <stdio.h>
const int Max = 3;
void main() {
	int var[] = { 1,2,34 };
	int i, * ptr;
	ptr = var;
	for (i = 0; i < Max; i++) {
		printf("var[%d]的值为%d\n", i, *ptr);
		ptr++;//ptr = ptr + 1;一个int字节数,即加4bit
	}
}

48、指针数组:让数组的元素,指向int或其他数据类型的地址(指针),可以使用指针数组
比如有10个字符串,分别保存在10个数组内,这时候就可以定义一个指针数组,每个数组元素为一个字符串数组指针,分别指向这10个字符串,操作起来就方便多了。

49、
1)一个指向指针的指针变量声明,必须在变量名前放置两个星号 int** ptr; //声明了一个指向int类型指针的指针
访问这个值为**p;
2)当函数的形参类型为指针类型时,,使用该函数需要传递指针、地址、或者数组给该形参;
3)数组本身就代表该数组首地址,因此传数组本身就是传地址

50、指针函数:函数的返回值是一个指针(地址)

#include <stdio.h>

char* strLong(char* str1, char* str2) {//未知字符串的长度,可以用指针数组的形式,要返回最长的字符串,可以选择指针函数
//需要从函数中返回多个值,此时使用数组或指针能够很好地完成这样的任务
	printf("第一个字符串的长度为%d,第二个字符串的长度为%d\n", strlen(str1), strlen(str2));
	return strlen(str1) >= strlen(str2) ? str1 : str2;
}

void main() {
	char str1[30], str2[30], * str;
	printf("请输入第一个字符串");
	gets(str1);
	printf("请输入第二个字符串");
	gets(str2);
	str = strLong(str1, str2);
	printf("更长的一个字符串是%s,长度为%d", str,strlen(str));
}

51、
1)指针作为函数返回值,在函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形参,函数返回的指针不能指向这些数据;
2)函数运行结束后会销毁该函数所有的局部数据,这里的销毁并不是将局部数据所占用的内存全部清零,而是程序放弃对它的使用权限,后面的代码可以使用这块内存;
3)C语言不支持在调用函数时返回局部变量的地址,如果需要,则定义局部变量为static变量

char * func (char a) {
    retrun &a;  //非法
}
#include <stdio.h> 
int* Pool(int array[], int size)
{
    int* x;
    int i = 0;
    int a[2] = { 0,1 };
    for (i = 0; i < size; i++)
    {
        a[0] += array[i]; //存储数组元素值的和  
        a[1] *= array[i]; //存储数组元素值的积  
    }
    //将数组的基地址赋值给整型指针  
    x = &a[0];
    //返回整个数组  
    return x;
}

int main()
{
    int a[] = { 1,2,3,4 };
    int* c; c = Pool(a, 4);
    Pool(a, 3);//增加这句就会让值改变,栈将数据重新覆盖
    printf("Sum = %d\nProduct = %d\n", c[0], c[1]);
    getchar();
    return 0;
}
Sum = 6
Product = 6

由于该数组是局部变量,因此在函数返回时其数组空间已经作废了,即指针应用一块无意义的地址空间,所以不会有返回值。
如果得到正常的值,只能是幸运的

退出函数的时候,系统只是修改了栈顶的指针,并没有清内存;
所以,是有可能正常访问到局部变量的内存的。
但因为栈是系统自动管理的,所以该内存可能会被分配给其他函数,这样,该内存的内容就会被覆盖;不再是原来的值了。

为了继续保存原来的数组,使main能继续操作数据,则可以使局部变量数组变为静态变量

static int a[2] = { 0,1 };
Sum = 16
Product = 144

编写一个函数,它会生成10个随机数,并使用表示指针的数组名(即第一个数组元素的地址)来返回它们。

#include <stdio.h>
#include <stdlib.h>

int *func(){
	static int arr[10];
	int i=0;
	for(i=0;i<10;i++){
		arr[i]=rand();
	}
	return arr;
} 

void main(){
	int *p=func();
	int i;
	for(i=0;i<10;i++){
		printf("%d\n",*(p+i));
	}
}

52、函数指针:指向函数的指针
例:用函数指针来实现对函数的调用,返回两个整数中的最大值

#include <stdio.h>
int max(int a, int b){
	return a>b?a:b;
}

void main(){
	int x, y, max Val;
	//说明函数指针
	//1.函数指针的名字pmax
	//2. int表示该函数指针指向的函数是返回int类型
	//3.(int, int)表示该函数指针指向的函数形参是接收两个int
	//4.在定义函数指针时,也可以写上形参名 int (*pmax)(int x, int y)= max;
	int (*pmax)(int , int) = max;//
	printf("Input two numbers:");
	
	scanf("%d %d",&x, &y);
	//(*pmax)(x, y)通过函数指针去调用函数max
	maxVal = (*pmax)(x, y);
	printf("Max value: 0%d pmax=%p pmax本身的地址=%p\n" , maxVal, pmax, &pmax);
	getchar();
	getchar();
}

53、回调函数:函数指针变量作为一个函数的参数来使用,回调函数就是通过函数指针调用的函数。

使用回调函数的方式,给一个整型数组int array[10]赋10个随机数

#include <stdio.h>

int initArray(int* array, int size, int(*func)(void)) {
	int i;
	for (i = 0; i < size; i++) {
		array[i] = func();
	}
}

int getRandomNumber(void) {
	return rand();
}

void main() {
	int i, array[10];
	initArray(array, 10, getRandomNumber);
	for (i = 0; i < 10; i++) {
		printf("%d\n", array[i]);
	}
	getchar();
}

54、动态创建数组,输入5个学生的成绩,另外一个函数检测成绩低于60分的,输出不合格的成绩

#include <stdio.h>
#include <stdlib.h>
void func(int *p) {
	int i;
	for (i = 0; i < 5; i++) {
		if (p[i] < 60) {
			printf("成绩不合格的有:%d", p[i]);
		}
	}
}

void main() {
	int *p, i;
	p = (int*)malloc(5 * sizeof(int));
	for (i = 0; i < 5; i++) {
		scanf("%d", p + i);
	}
	func(p);
}

55、指针使用一览
在这里插入图片描述

56、什么时候用指针
1) 需要改变实参的时候, 只能用指针.
2)传递大型结构并且"只读"其元素的时候,
因为大型结构通过值传递, 需要拷贝其每个元素, 这样效率太低.
3) 需要遍历数组或频繁引用其元素时, 这样效率比使用下标高.
4)动态分配空间时, 必须使用指针.
5)传递数组时, 必须使用指针.
6) 函数返回指针时, 比如fopen[object Object]

56、结构体

struct Student {
	char* name;
	int age;
	char group;
};

结构体成员的类型:基本类型、数组或指针、结构体

在创建一个结构体变量后,需要给成员赋值,否则会导致程序异常终止

#include <stdio.h>
void main() {
	struct Student {
		char* name;
		int age;
		char group;
	};

	struct Student s1;  //没有初始化结构体变量的成员
	printf("名字:%s", s1.name);
}

定义方式

//方式一
struct Cat{
	...
};
struct Cat cat1,cat2;

//方式二
struct Cat{
	...
}cat1,cat2;

//方式三
struct{
	...
}cat1,cat2;

赋值方式

struct Cat{
	char* name;
	int age;
}cat1={'a',10},cat2={'b',20};

struct Cat cat3={'c',30};
struct Cat cat4;
//cat4={'d',40};//不能这样赋值
cat4.name='d';
cat4.age=40;

57、结构体举例1

#include <stdio.h>

struct Dog {
	char* name;
	int age;
	double weight;
};

char* func(struct Dog dog) {
	static char info[50];
	snprintf(info, "名字:%s 年龄:%d 体重:%.2f", dog.name, dog.age, dog.weight);
	dog.name = 'c';
	return info;
}

void main() {
	struct Dog dog;
	dog.name = 'a';
	dog.age = 10;
	dog.weight = 12.3;
	char* info = NULL;
	info = func(dog);
	printf("小狗的信息:%s", info);
	printf("小狗的名字:%s", dog.name);
	getchar();
}

结构体举例2

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

struct Visitor {
	char name[10];
	int age;
	double pay;
};

void ticket(struct Visitor* visitor) {
	if ((*visitor).age > 18) {
		(*visitor).pay = 20;
	}
	else {
		(*visitor).pay = 0;
	}
}

void main(){
	struct Visitor visitor;
	while (1) {
		printf("请输入游客的名字:");
		scanf("%s", visitor.name);
		if (!strcmp("n", visitor.name)) {
			break;
		}
		printf("请输入游客的年龄:");
		scanf("%d", &visitor.age);
		ticket(&visitor);
		printf("游客的票价应该为%.2f\n", visitor.pay);
	}
}

58、共用体Union
属于构造类型,可以包含多个类型不同的成员,也被称为联合或者联合体

union data{
	...
};

共用体与结构体的区别:结构体的各个成员会占用不同的内存,互相之间没有影响,共用体的所有成员占用一段内存,修改一个成员会影响其余所有成员。

59、文件操作
1)getchar()与putchar()函数
程序会读取一个单一的字符

#include <stdio.h>
void main() {
	int c;
	printf("输入值:");
	c = getchar();

	printf("结束");
	putchar(c);
}

2)gets()与puts()函数
程序会读取一整行直到改行结束

#include <stdio.h>
void main() {
	char str[10]; //超出长度报异常
	printf("输入值:");
	gets(str);

	printf("结束");
	puts(str);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值