c语言知识总结

C语言保姆级攻略

C语言是一种通用的高级编程语言,广泛应用于系统软件开发、嵌入式系统和游戏开发等领域。以下是一些C语言的基本知识总结:

 数据类型

用不同数据类型所定义的变量所占空间大小不一样,定义的变量不是保存于数据类型中,而是因为只有定义了该数据类型的变量才能保存数据。


基本数据类型:整型、浮点型、字符型。

一、整型

在C语言中,整型是一种基本的数据类型,用于表示整数。C语言中有四种整型:int、short、long和long long,其大小和范围分别如下:

1. int类型:

大小:通常为4个字节(32位)
范围:-2,147,483,648 到 2,147,483,647

2. short类型:

大小:通常为2个字节(16位)
范围:-32,768 到 32,767

3. long类型:


大小:通常为4个字节(32位),也可能为8个字节(64位)
范围:-2,147,483,648 到 2,147,483,647

4. long long类型:


大小:通常为8个字节(64位)
范围:-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807

在使用整型变量时,可以使用以下运算符:+、-、*、/、%;其中,%表示取余数,也称为模运算。总的来说,C语言的整型数据类型大小和范围都相对比较简单,开发者们只需要根据实际需要选择不同类型的整型即可。

二、浮点型

浮点型是一种用于表示小数的数据类型,它可以用于存储非整数的数字,如小数和科学计数法表示的数字。在计算机中,浮点型数据是通过使用一定数量的二进制位来表示实数的。以下是浮点型的一些总结:

  1. 浮点型数据类型分为单精度浮点型(float)和双精度浮点型(double)。

  2. 单精度浮点型占用4个字节,双精度浮点型占用8个字节。

  3. 浮点型数据在内存中以二进制形式存储。在单精度浮点型中,使用23个二进制位来表示小数位,8个二进制位来表示指数位;在双精度浮点型中,使用52个二进制位来表示小数位,11个二进制位来表示指数位。

  4. 浮点型数据的存储格式还涉及符号位,这意味着浮点数可以是正数或负数。

  5. 由于浮点型使用二进制来表示实数,所以在进行运算时可能会出现舍入误差,因此在需要高精度计算时,应该使用其他数据类型。

总之,浮点型是一种方便表示小数的数据类型,但在进行精确计算时需要注意舍入误差的问题。

三、字符型

字符型是一种数据类型,表示单个字符。在计算机编程中,字符通常用ASCII码或Unicode码表示。在多数编程语言中,字符型变量用单引号括起来,例如 'A'。

字符型变量可以用在很多地方,比如用于表示字符串的单个字符、用于密码输入、用于显示图形界面中的按钮或标签等。在某些编程语言中,字符型变量可以和整型变量相互转化,例如C语言中的ASCII码和整型的相互转化。

char grade='A';

char grade=65;

复合数据类型:数组、结构体。

一、数组

一组数据类型相同的元素的集合;每个元素的数据类型 数组名[元素的个数];

数组传参发生降维,降维成指向其(数组)内部元素类型的指针。

1、数组的创建与初始化

创建数组时数组空间为整体开辟整体释放,在内存中是连续存放,在定义时就已经确定数组大小(下标不可为0),且不可被整体赋值。在数组的创建过程中,如果进行了初始化则可不指定数组的大小,多维数组按照一维数组进行理解。

传参发生降维,降维成指向其(数组)内部元素类型的指针。

​ 数组名一般情况下都指的是首元素的地址,但如果sizeof()单独出现以及&后跟数组名时表示的是整个数组

int s[5];
//表示数组首元素地址
printf("%d\n", sizeof(s+1));//结果为4/8,指针的具体大小根据编译器的不同大小不同
//表示整个数组
printf("%d\n", sizeof(s));//结果为20
2.数组传参(函数)

由于在传参过程中如果拷贝整个指针会导致效率大大降低甚至导致栈溢出,所以数组传参要发降维问题,函数内数组作为参数时,实参为首元素地址,形参为指针。

在访问结构体成员时也同样要发生类似的操作,用指向结构体的指针来指代结构体。

typedef struct node{
 int a;
 int b;
}point;

void pop(int* p){
	
}

int main(){
	point a;
	int* p=a;
	pop(p);
	return 0;
}

​ 传参样例

//用数组的形式传递参数,不需要指定参数的大小,传的只是数组首元素的地址。
void test(int arr[])
{}

//也可以传入数组大小
void test(int arr[10])
{}

//数组降维,用指针进行接收,传的是数组首元素的地址
void test(int *arr)
{}

//二维数组传参,第一个方括号可以空,但是第二个不可以空
void test(int arr[][5])
{}

void test(int arr[4][5])
{}

//传过去的是二维数组的数组名,即数组首元素的地址,也就是第一行的地址,第一行也是个数组,用一个数组指针接收

二、结构体

定义结构体:包括结构体名称和成员变量。结构体访问:通过.运算符访问结构体中的成员变量。
结构体作为函数参数:可以将结构体作为参数传递给函数。

1、基本定义

①结构体不可以自引用!但可以在结构体内定义该结构体类型的指针;

​②定义结构体本质是新增一种类型;

​ ③结构体传参要传结构体地址(指针),以此提高效率。

struct node1{
	int  a;
	int  b;
};

typedef struct node2 {
	int c;
	int d;
}node2;//通过typedef为结构体变量定义一个别名node2,在以后使用中可以使用别名以此提高编写效率

int main(){
	struct node1 s;//用结构体定义变量
	node2  q;//用别名定义变量
}
2.结构体变量的定义和初始化
struct Point1
{
	int x;
	int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2

typedef struct Point2
{
	int x;
	int y;
}p;
p p1;
p p2;

在定义结构体变量时,可以在初始化的部分定义其内容,也可以在之后定义。

typedef struct Point2
{
	int x;
	int y;
}p;
p p1;
p1.x=1;
p1.y=2;//可以直接用结构体类型的变量进行定义

typedef struct Point2
{
	int x;
	int y;
}p;
p* p2=(p*)malloc(sizeof(p));
p2->x=1;
p2->y=2;//定义一个指向结构体的指针并为其分配空间即可进行定义
3、结构体的内存对齐(结构体的占用大小的计算)


结构体内存空间占用的大小并不是单纯的元素相加,而是通过浪费一定量的空间来换取目标数据读取速度的加快

计算方式:

① 第一个成员在与结构体变量偏移量为0的地址处。

② 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

​ (起始偏移量要能整除该成员的对齐数)

对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
​ VS中默认的值为8

③ 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

④ 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处(即结构体大小),结构体的整

体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
 

三大结构


一、顺序结构

  顺序结构就是从头到尾一句接着一句的执行下来,执行完上一个语句就会自动执行下一个语句,是无条件的,不必做任何判断,直到执行完最后一句程序结束。

例:从键盘输入一个大写字母,要求改用小写字母输出。

#include<stdio.h>
int main()
{
    char x, y;
    scanf("%c", &x);
    if (x >= 'A' && x <= 'Z')
    {
        y = x + 32;
        printf("%c\n", y);
    }
    else
    {
        printf("this is a erro\n");
    }
    return 0;
}

这段代码属于典型的顺序结构,定义一个字符型变量x和y,从键盘中读取x,如果x的值为大写字母,则根据大写字母和小写字母的ASCLL码相差32,将大写字母+32得到小写字母(存入字符变量y中),并输出y的值,程序结束;如果输入x的值不是大写字母而是其他值,则会进入else语句,输出:this is a erro,程序结束。


二、选择结构

选择结构就是到某个节点后,会根据一次判断的结果来决定之后程序向哪一个分支方向执行。

1、用if语句实现选择结构

 

if语句的一般形式:
(1)    if (表达式)        语句1             (没有else子句部分)
(2)    if (表达式)                         (有else子句部分)
           语句1
       else
           语句2
(3)    if(表达式1)         语句1            (在else部分又嵌套了多层的if语句)
       else if(表达式2)    语句2
       ...                 ...
       else if(表达式m)    语句m
       else                语句m+1

例:输入三个数a,b,c,要求按有小到大的顺序输出。

#include<stdio.h>
int main()
{
    float a, b, c, tmp;
    scanf("%f %f %f", &a, &b, &c);
    if (a > b)    //判断a和b的大小
    {
        tmp = b;  //借助变量tmp,实现变量a和变量b互换值
        b = a;
        a = tmp;
    }             //互换后,a小于或等于b
    if (a > c)    //判断a和c的大小
    {
        tmp = c;  //借助变量tmp,实现变量a和变量c互换值
        c = a;
        a = tmp;
    }             //互换后,a小于或等于c
    if (b > c)    //判断b和c的大小
    {
        tmp = c;  //借助变量tmp,实现变量b和变量c互换值
        c = b;
        b = tmp;
    }            //互换后,b小于或等于c
    printf("%5.2f %5.2f %5.2f\n", a, b, c);  //顺序输出a,b,c的值
    return 0;
}

在经过第1次互换值后,a<=b,经过第2次互换值后a<=c,这样a己是三者中最小的(或最小者之一),但是b和c谁大还未解决,还需要进行比较和互换。经过第3次互换值后,a<=b<=c,此时,a,b,c3个变量已按由小到大顾序排列。顺序输出a,b,c的值即实现了由小到大输出 3个数。
三、循环结构:重复执行特定的代码块,使用for、while或do-while循环。

2.用switch语句实现多分支选择结构

switch语句是多分支选择结构,用来实现如下图所表示的多分支选择结构。

switch语句的一般形式如下:
 
switch (表达式)
{
    case 常量1 : 语句1
    case 常量2 : 语句2
    ...  ...      ...
    case 常量n : 语句n
    default    : 语句n+1
}

要求按照考试成绩的等级输出百分制分数段,A等为85-100分,B等为70-85分,C等为60-69分,D等为60分以下。成绩由键盘输入。

#include<stdio.h>
int main()
{
    char grade;
    scanf("%c", &grade);
    printf("Your score:");
    switch (grade)
    {
    case 'A':printf("85-100\n"); break;
    case 'B':printf("70-84\n"); break;
    case 'C':printf("60-69\n"); break;
    case 'D':printf("<60\n"); break;
    default: printf("enter data error!\n");
    }
    return 0;
}

 等级grade 定义为字符变量,从键盘输入一个大写字母,赋给变量grade,switch 得到 grade 的值并把它和各case 中给定的值(A',B',’C,'D'之一)相比较,如果和其中之一相同(称为匹配),则执行该case 后面的语句(即 printf 语句)。输出相应的信息。如果输入的字符与'A',B',’C,‘D'都不相同,就执行 default 后面的语句,输出“enter data error!'的信息。注意每个case后面的语句中,最后都有一个break语句,它的作用是使流程转到switch语句的末尾,即结束switch语句。


函数

 
一、库函数

C语言基础库中的函数,在添加头文件后可直接调用。


二、自定义函数

1、函数组成

由函数名、返回值类型、函数参数以及函数体组成。

​ 实参:真实的传入函数的变量,在被调用时会发生临时拷贝,并非把原来的变量直接放入函数中,只是把实参的数据拷贝给形参。

​ 形参:函数名括号后的变量,因为形参只有在被调用的时候才被实例化并分配空间(形参实例化),在被调用过后即被销毁,只在该函数中有效(局部变量),所以叫形参。

//函数定义
double	Add(double x, double y){
	return x+y;
}
//函数声明
double 	Add(double x, double y);
2、函数调用

 分为传值调用与传址调用,其中传址调用是把函数外部创建的内存地址传递给函数,可以真正与原值建立起联系,直接操纵函数外部的变量。

#include <stdio.h>
void new_line()
{
	printf("hehe\n");
}
void three_line()
{
	int i = 0;
	for(i=0; i<3; i++)
	{
		new_line();
	}
}
int main()
{
	three_line();
	return 0;


指针

指针定义:保存内存地址的变量。指针运算:通过指针间接访问内存中的值,可以进行指针的加减运算。指针与数组:指针可以与数组一起使用,方便对数组的操作。

指针变量是个变量,指针本身是个地址,用于存放内存单元的地址。 指针时用来存放地址的,指针类型的变量无论指向目标是什么类型,指针本身在32位平台所占大小都为4个字节,在64位平台是8个字节 。

一、指针的解引用

 1、对指针的解引用只能看到sizeof(type)个字节的数据;

​ 2、按字节为单位,数据有高权值和低权值之分,地址有低地址和高地址之分;

​ 3、数据低权值位在低地址处即为小端存储,反之则为大端存储。

二、野指针

概念

​ 指向的位置是不可知的指针。

规避

​ 1、指针在定义时就进行初始化;

​ 2、避免指针越界(eg:注意循环时循环次数的限制);

​ 3、指针使用完即进行指针指向空间释放;

​ 4、避免返回局部变量的地址;

​ 5、指针使用前检查其有效性。

三、指针运算

1、指针±整数,等价于±sizeof(type);

​ 2、指针-指针,两指针必须同一类型,一般用于数组与字符串求其两地址间相隔的单元格数,否则无效(指针+指针为非法操作);

​ 3、指针的关系运算。

​ 4、指针和数组都可以用中括号或者解引用(二者互通)。

四、字符指针

在指针的类型中我们知道有一种指针类型为字符指针 char* ;

一般使用方法

int main()
{
	char ch = 'w';
	char *pc = &ch;
	*pc = 'w';
	return 0;
}


用char*指针指向字符串

int main()
{
	const char* pstr = "hello bit.";
	printf("%s\n", pstr);
	return 0;
}
//上述代码中把字符串hello bit.的首地址放入了指针中

五、数组指针

1、数组指针定义

指针数组本质上是指针,该类指针指向的是一个数组

int (*p)[10]; //解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个 指针,指向一个数组,叫数组指针

2、数组指针&数组

首先需要看的是数组名与&数组名(可以等价于数组指针)之间的关系

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("arr = %p\n", arr);
	printf("&arr= %p\n", &arr);
	printf("arr+1 = %p\n", arr+1);
	printf("&arr+1= %p\n", &arr+1);
	return 0;
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值