c语言学习笔记

数据类型

无符号数和有符号数

在正常编码没有明确指出时候,默认为有符号数。关键字为signed。

无符号变量输入负数会发生模数运算

#include<stdio.h>

int main(){
	unsigned short f= -20;
    printf("%u/n",f);
    
    return 0;
}

标识符命名规则

1.标识符只能由数字、字母、下划线(_)组成

2.标识符不能以数字开头

3.标识符不能是关键字比如if

4.标识符区分大小写

隐式转换规则

规则一

取值范围小的,和取值范围大的计算,小范围会自动转换为大范围

int a =10;
double b =12.3;
c = a + b;

从大到小

double ,float ,long long ,long ,int , short, char
规则二

short char 类型的数据,在运算的时候先提升为int,再计算

强制类型转换

格式:目标数据类型 变量名=(目标数据类型)被强转的数据;

当大的数据类型转换为小的数据类型的时候发生强制类型转换。

运算符

自增自减运算符
  1. 单独使用, ++和–放在前面和后面作用一样
  2. 参与计算

++在后面

int a = 10;
int b = a++;
//此时的b等于10

++在前面

int a = 10;
int b = ++a;
//此时b为11
#include<stdio.h>

int main(void){
	int i = 10;
	int j = 5;
	int k = i++ + ++i - --j - i--;//10+11-4-10
	printf("%d\n", k);
	/*windos系统结果为7
    前缀优先于后缀,前缀统一自增、自减,再把结果拿来用
    后缀统一先用,等整个表达式变量用完了再进行自增
	*/
	return 0;

}
#include<stdio.h>

int main(){
    int a = 10;
    int k1 = a++ + a++;//先用后加,加完进行下一行计算
    int k2 = ++a + ++a;
    int k3 = ++a + a++;
    int k4 = a++ + ++a;
    printf("%d\n", k1);//20
    printf("%d\n", k2);//28
    printf("%d\n", k3);//30
    printf("%d\n", k4);//34
    
    return 0;
}
赋值运算符
符号说明举例
=直接赋值int a=10,将10赋值给a
+=加后赋值a+=b,将a+b的值给a
-=减后赋值a-=b,将a-b的值给a
*=乘后赋值a*=b,将a*b的值给a
/=除后赋值a/=b,将a/b的值给a
%=取余赋值a%=b,将a除以b的余数给a
关系运算符

通常是比较,比如==就是比较两个数是否相等,相等为0,不相等为1。

逻辑运算符
符号作用说明
&&两边都为真,结果才为真
||两边都为假,结果才为假
取反
三元运算符

格式:关系表达式?表达式1:表达式2;

例如

a > b ? a : b;
//当a > b为真的时候,执行a:b的a;当a > b为假的时候,执行a:b的b式子

分支语句

if
	if(判断句1){

	}else if(判断句2)
    {
		...
	}else
    {

	}
switch语句

格式:

switch(){
	case 1:
		语句1;
		break;
	case 2:
		语句2:
		break;
	...
	default:
		语句体n;
		break;
}

细节说明:

  • 表达式:计算结果只能为(字符/整数)
  • case:值只能是(字符/整数)的字面量,不能是变量
  • case:值不允许重复
  • break:表示中断,结束的意思,结束switch语句
  • default:所有情况都不匹配,执行该处的内容

循环结构

for循环语句
for (初始化语句;条件判断语句;条件控制语句){
	循环体;
}

求一到五的和

#include<stdio.h>

int main(void){
    
	int sum = 0;//定义一个变量用于累加数据
	
	for (int i = 1; i <=5; i++)
	{
		sum = i + sum;
	}
    //打印sum
	printf("%d\n", sum);//15
	
	return 0;
}
while循环
初始化语句
while(条件判断语句)
{
	循环体语句
	条件控制语句
}

for和while的对比

 相同点:运行规则是一样的
 不同点:1. for循环中,控制循环的变量,因为归属for循环的	  语法结构中,在for循环结束后,就不能再次被访问到。
   	   2. while循环中,控制循环的变量,对于while循环来	   说不归属其语法结构中,在while循环结束后,该变量还        可以继续使用。
do…while循环
初始化语句;
do{
	循环体语句;
	条件控制语句;
}while(条件判断语句);
//先执行后判断,循环体至少执行一次
continue
#include<stdio.h>

int main(){
	//跳过第三个
	for(int i = 1; i <= 5; i++)
	{
		if(i == 3)
		{
			continue;
		}
		printf("%d\n", i);
	}
}
循环嵌套
#include<stdio.h>

int main(){
	//跳过第三个
	for(int i = 1; i <= 5; i++)
	{
		for(int j = 1; j <= 5; j++)
		{
			printf("内循环执行%d\n", j);
		}
		printf("内循环结束\n");
		
	}
	printf("外循环结束\n");
	
	return 0;
}
跳出循环语句
break

跳出循环

#include<stdio.h>

int main(){
	//跳过第三个
	for(int i = 1; i <= 5; i++)
	{
		for(int j = 1; j <= 5; j++)
		{
			printf("内循环执行%d\n", j);
            break;//跳出内循环
		}
		printf("内循环结束\n");
		
	}
	printf("外循环结束\n");
	
	return 0;
}

结果

内循环执行1
内循环结束
内循环执行1
内循环结束
内循环执行1
内循环结束
内循环执行1
内循环结束
内循环执行1
内循环结束
外循环结束
continue

continue跳过当前循环

#include<stdio.h>

int main(){
	//跳过第三个
	for(int i = 1; i <= 5; i++)
	{
		for(int j = 1; j <= 5; j++)
		{
			printf("内循环执行%d\n", j);
			continue;
		}
		printf("内循环结束\n");
		
	}
	printf("外循环结束\n");
	
	return 0;
}

结果

内循环执行1
内循环执行2
内循环执行3
内循环执行4
内循环执行5
内循环结束
内循环执行1
内循环执行2
内循环执行3
内循环执行4
内循环执行5
内循环结束
内循环执行1
内循环执行2
内循环执行3
内循环执行4
内循环执行5
内循环结束
内循环执行1
内循环执行2
内循环执行3
内循环执行4
内循环执行5
内循环结束
内循环执行1
内循环执行2
内循环执行3
内循环执行4
内循环执行5
内循环结束
外循环结束
goto
#include<stdio.h>

int main(){
	//跳过第三个
	for(int i = 1; i <= 5; i++)
	{
		for(int j = 1; j <= 5; j++)
		{
			printf("内循环执行%d\n", j);
			goto a;
		}
		printf("内循环结束\n");
		
	}
	a: printf("外循环结束\n");
	
	return 0;
}

goto跳到指定语句

内循环执行1
外循环结束

函数

带有参数函数
#include<stdio.h>

void sum(int num1, int num2)//形参
{
	int sum = num1 + num2;
	printf("%d\n", sum);
}
int main(){
	sum(30 , 40);//实参
	sum(10 , 20);
	
	return 0;
}
带有返回值函数
#include<stdio.h>

double getArea(double len, double width)
{
    double area = len* width;
    return area;
}
int main(){
	//函数比较两个长方形面积大小
    double area1 = getArea(5.3, 2.7);//调用函数,接受参数
	double area1 = getArea(1.5, 0.8);
    if (area1 > area2)
    {
        printf("1");
    }
    else if(area1 < area2)
    {
        printf("2");
    }
    else
    {
        printf("0");
    }
	return 0;
}
函数注意事项
  • 函数不调用不执行
  • 函数名不能重复
  • 函数与函数之间是平级关系,不能嵌套定义
  • 自定义函数写在main函数下面时候需要在上方声明函数
  • return下面不能编写代码,执行不到
  • 函数返回值类型为void时候,表示没有返回值,return可以省略不写,如果书写return,后面不能跟具体数据,仅仅表述接二叔函数
常见函数和其头文件
#include<math.h>
int main{
	pow();
	sqrt();
	fabs();
}
stdio.h

	printf();
	scanf();
stdlib.h
	
	malloc()
	free()
	calloc()
	realloc()
	rand()
string.h
	
	strlen()
	strcmp()
	strlwr()
	strupr()
	strcat()
	strcpy()
time.h
	
	time()//获取当前时间
#include<stdio.h>
#include<time.h>

int main{
	//time小括号中表述当前时间是否需要在其他地方进行存储
	//返回值long long
	//从1978年1月1日开始
	long long res = time(NULL);
	printf("%lld\n", res);
	
	return 0;
}

数组

数据类型 数组名[长度]

数组初始化

定义数组时候,第一次给数组赋值。

//数据类型 数组名[长度] = {数据值,数据值...}
int arr[] = {1,2,3};//长度省略时候,数据值的个数就是数组长度。
double arr[2] = {0.0};//不知道具体的,只给一个值,剩下的值都为默认0,double是0.0,int是0
索引
  • 索引就是数组的一个编号,也叫角标、下标、编号
  • 特点:从0开始的,连续+1,不间断
#include<stdio.h>

int main()
{
	int arr[] = {1, 2, 3, 4, 5};
    //获取索引为0,2,4的元素,并求和
    int num1 = arr[0];
    int num2 = arr[2];
    int num3 = arr[4];
    int sum = num1 + num2 + num3;//1 + 3 + 5
    printf("%d\n", sum);
    printf("修改前:%d\n", arr[4]);
    arr[4] = 10;//修改下标为4的数据
    printf("修改后:%d\n", arr[4]);
	return 0;
}


数组遍历
#include<stdio.h>

int main()
{
	int arr[5] = {1,2,3,4,5};
    //利用循环遍历数组
    for (int i = 0; i <=4 ; i++)
    {
        printf("%d\n", arr[i]);
    }
	return 0;
}

数组在内存中存储方式
#include<stdio.h>

int main()
{
    //获取数组的内存地址
    int arr[] = {1,2,3};
    printf("%p\n", &arr);//数组首地址
    printf("%p\n", &arr[0]);//[]中的表示偏移量
    printf("%p\n", &arr[1]);//内存角度:首地址+偏移量
    printf("%p\n", &arr[2]);//
    //结果:00BEF9D8
    //00BEF9D8
    //00BEF9DC
    //00BEF9E0
    //差一个int类型的长度
	return 0;
}
数组注意事项

数组作为参数时候需要获取数组长度

#include<stdio.h>
void printArr(int arr[]);

int main()
{
    //获取数组的内存地址
    int arr[] = {1,2,3};
    //数组作为函数的参数,实际上传递的是数组的首地址,如果在在函数中对数组进行遍历,一定要把数组长度一起传递过去
    //定义处arr表示的就是完整的数组
    //函数中的arr只是一个变量,用来记录数组的首地址
	printArr(arr);
	return 0;
}
void printArr(int arr[])
{
	int len = sizeof(arr)/sizeof(arr[0]);
	for (int i= 0; i <= len; i++)
	{
		printf("%d\n", arr[i]);
	}
}
#include<stdio.h>
void printArr(int arr[], int len);

int main()
{
    int arr[] = {1,2,3};
    int len = sizeof(arr) / sizeof(int);
	printArr(arr, len);
	return 0;
}
void printArr(int arr[], int len)
{
	for (int i= 0; i <= len; i++)
	{
		printf("%d\n", arr[i]);
	}
}

数组练习

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

int main()
{
	//生成10个1-100之间的随机数存入数组
	//求出所有数据的和
	int arr[10] = { 0 };
	int len = sizeof(arr) / sizeof(int);
	//生成10个1-100之间的随机数存入数组
	//设置种子
	//生成随机数
	srand(time(NULL));//srand函数是用于生成随机数种子,决定随机数序列的起始值,用于每次生成的随机数序列不同。
	for (int i = 0; i < len; i++)
	{
		int num = rand() % 100 + 1;
		//随机数存入数组
		printf("%d\n", num);
		arr[i] = num;
	}
	//利用累加思想求和
	int sum = 0;
	for (int i = 0; i < len; i++)
	{
		sum = sum + arr[i];
	}
	printf("%d\n", sum);

	return 0;
}

反转数组

#include<stdio.h>
void printArr(int arr[], int len);

int main() 
{
	int arr[5] = { 0 };
	int i = 0;
	int len = sizeof(arr) / sizeof(int);//长度
	for (i;i<len ; i++)
	{
		printf("输入第%d个数\n", i+1);
		scanf_s("%d", &arr[i]);//存入数组
	}
	printArr(arr, len);
	printf("反转数组后:\n");
	int j = 0;
	int k = len - 1;
	while (j < k) 
	{
		int temp = 0;//临时的第三方变量,用于交换数据
		temp = arr[j];//反转数组
		arr[j] = arr[k];
		arr[k] = temp;
		j++;
		k--;
	}


	printArr(arr, len);

	return 0;
}

void printArr(int arr[],int len)
{	//函数打印数组
	for (int i = 0; i<len; i++)
	{
		printf("%d\n", arr[i]);
	}

}
数组查找

顺序查找

#include<stdio.h>
int order(int arr[], int len, int num);

int main()
{
	int arr[5] = { 1,1,3,4,4 };
	int i = 0;
	int len = sizeof(arr) / sizeof(int);//长度
	int num = 1;
    //index:-1表示无匹配数据,其他数字表示索引,索引是从零开始
	int index = order(arr, len, num);
	printf("%d\n", index);
	//缺点是数组中如果有多个相同数据时,只能返回第一个
	//在函数中遇到第一个匹配值就返回index值
	//终止函数继续循环
    //如果想要解决,需要把索引存入数组,函数返回数组指针。
	return 0;
}

int order(int arr[], int len,int num )
{
	int ar[10] = { 0 };
	for (int i = 0; i < len; i++) 
	{
		if (arr[i] == num) {
			//ar[i] = i;
			return i;
		}
	}
	return -1;
}

指针

指针
#include<stdio.h>

int main()
{
    //利用指针获取数据
    int a = 10;
    //定义一个指针变量指向变量a
    int* p = &a;//这个*是一个指针标记和下方做区别
    printf("%d\n", *p);
    //*p中的*表示解地址
    *p = 200;//利用指针修改数据
    printf("%d\n", *p);
    
	return 0;
}
  • 指针变量名字是p
  • 指针数据类型要和指向变量的类型保持一致
  • 指针变量占用的大小,跟数据类型无关,跟编译器有关,32位的是4字节,64位是8字节
指针的作用
  • 操作其他函数中的变量
  • 函数返回多个值
  • 函数的结果和计算状态分开
#include<stdio.h>
void swap(int* num1, int* num2);

int main()
{
    //定义两个变量,交换变量中记录的值
    int a = 10;
    int b = 20;
    int* p1 = &a;
    int* p2 = &b;
    printf("%d, %d\n", a, b);
    swap(p1, p2);
    printf("%d, %d\n", a, b);

    return 0;
}
void swap(int* num1, int* num2)
{//如果用变量,只会传递a,b变量的值。
    int temp = *num1;
    *num1 = *num2;
    *num2 = temp;
}

指针作用细节

函数中变量的生命周期跟函数相关,函数结束,变量也会消失,此时在其他函数中,就无法通过指针使用。如果不想函数中的变量被回收,可以在变量前面加static关键字变成静态指针,也叫全局指针。

#include<stdio.h>

int main()
{
    int* p = method();
   	printf("time");
   	printf("%d\n", *p);//不能打印,method函数结束后,该函数里面所有的变量也会随之消失
	return 0;
}

int* method()
{
    int a = 10;//在前面加上static关键字就可以不被删除
    return &a;
}

作用二:返回多个数据

#include<stdio.h>

void getmax(int arr[], int len, int* max, int* min);

int main()
{
    //定义一个函数,求最大值和最小值
    int arr[] = { 1, 2, 3 };
    int len = sizeof(arr) / sizeof(int);
    int max = arr[0];
    int min = arr[0];
    getmax(arr, len, &max, &min);//函数传递max和min的地址
    printf("%d,%d", max, min);
    return 0;
}
void getmax(int arr[], int len, int* max, int* min)
{
    for (int i = 0; i < len; i++)
    {
        if (arr[i] > *max)
        {
            *max = arr[i];
        }
    }
    for (int i = 0; i < len; i++)
    {
        if (arr[i] < *min)
        {
            *min = arr[i];
        }
    }
}

作用三:函数结果和计算状态分开

#include<stdio.h>
int getreminder(int a, int b, int* res);

int main()
{
    //定义一个函数,求两个数的余数
    int a = 10;
    int b = 3;
    int res = 0;//定义一个数用于接受余数
    int flag = getreminder(a, b, &res);//传递参数只用传递res的地址
    if (!flag) 
    {
        printf("余数为:%d\n", res);
    }
    return 0;
}

int getreminder(int a, int b,int* res)//表示res是个指针变量
{
    if (b == 0)
    {
        return 1;
    }
    *res = a % b;//解引用res。改变res地址的值
    return 0;
}
野指针和空指针
#include<stdio.h>

int* method();
int main()
{
    //野指针,指向空间未分配
    int a = 10;
    int* p = &a;
    printf("%p\n", p);
    printf("%d\n", *p);
    int* p1 = p + 10;
    printf("%p\n", p1);
    printf("%d\n", *p1);
    //悬浮指针,指针地址空间已经被释放
    int* p2 = method();
    printf("asdf\n");//拖时间,如果没有这句,函数定义的局部变量还没有被释放
    printf("%p\n", p2);
    printf("%d\n", *p2);

    return 0;
}
int* method()
{
    int num = 10;
    int* p = &num;
    return p;
}
结果:
0116F72C
10
0116F754
23165464
asdf
0116F624
10098975
二级指针和多级指针
#include<stdio.h>

int main()
{
	int a = 10;
	int b = 20;
	int* p = &a;
	//定义二级指针
	int** pp = &p;
	//作用一:利用二级指针修改一级指针里面记录的内存地址
	printf("%p\n", p);
	*pp = &b;
	//作用二:利用二级指针获取到变量中记录的数据
	printf("%p\n", &a);
	printf("%p\n", &b);
	printf("%p\n", p);
	printf("%d\n", **pp);

	return 0;
}
结果:
008FFD98
008FFD98
008FFD8C
008FFD8C
20
数组指针
#include<stdio.h>

int main()
{
	int arr[] = { 10,20,30,29,32 };
	int len = sizeof(arr) / sizeof(int);
	//获取数组指针
	// 实际上获取的数组首地址
	int* p1 = arr;
	int* p2 = &arr[0];
	for (int i = 0; i < len; i++) {
		printf("%d\n", *p1);
		p1++;
		//或者表达为一行printf("%d\n", *p1++);
		
	}
	

	return 0;
}

结果:

10
20
30
29
32

数组指针细节

arr参与计算的时候,会退化为第一个元素的指针

特殊情况,利用sizeof计算整个数组大小时候,不会退化,arr还是整体。

二维数组
两种定义方式
遍历

用索引遍历第一种二维数组

#include<stdio.h>

int main()
{
	//定义二维数组
	int arr[3][5] = {
		{1,2,3,4,5},
		{11,22,33,44,55},
		{12,23,43,54,15},
	};
	//利用索引方式进行遍历
	//arr[0]:表示二维数组中的第一个一维数组,{1,2,3,4,5}
	//arr[1]:表示二维数组中的第二个一维数组,{11,22,33,44,55}
	//arr[2]:表示二维数组中的第三个一维数组,{12,23,43,54,15}

	for (int i = 0; i < 3; i++) 
	{
		//依次表示二维数组中的索引
		for (int j = 0; j < 5; j++)
		{
			//j:依次表示一位数组中的索引
			//内循环:遍历每一个一维数组
			printf("%d ", arr[i][j]);
		}
		//当内循环结束之后,表示一维数组遍历完成
		printf("\n");
	}
	

	return 0;
}

遍历第二种索引

#include<stdio.h>

int main()
{
	//第二种方法定义二维数组
	int arr1[] = { 1,2,3 };
	int arr2[] = { 2,12,13,14 };
	int arr3[] = { 1,321,312,32,24,42 };
	int* arr[3] = { arr1,arr2,arr3 };//存入的是三个一维数组的地址
	//利用索引方式进行遍历
	//再定义一个数组,存所有数组长度
	int len1 = sizeof(arr1) / sizeof(int);
	int len2 = sizeof(arr2) / sizeof(int);
	int len3 = sizeof(arr3) / sizeof(int);

	int lenArr[3] = { len1,len2,len3 };
	for (int i = 0; i < 3; i++) 
	{
		//依次表示二维数组中的索引
		for (int j = 0; j < lenArr[i]; j++)
		{
			//j:依次表示一位数组中的索引
			//内循环:遍历每一个一维数组
			printf("%d ", arr[i][j]);
		}
		//当内循环结束之后,表示一维数组遍历完成
		printf("\n");
	}
	

	return 0;
}
函数指针
#include<stdio.h>

void method1();
int method2(int a, int b);

int main()
{
	//函数指针
	//定义指针指向两个函数
	void (*p1)() = method1;
	int (*p2)(int, int) = method2;
	//利用函数指针调用函数
	p1();
	int nu = p2(1, 2);
	printf("%d\n", nu);
	return 0;
}

void method1()
{
	printf("method1\n");
}
int method2(int a, int b)
{
	printf("method2\n");
	int num = a + b;
	return num;
}

结果

method1
method2
3

字符串

定义与遍历
#include<stdio.h>


int main()
{
	
	//定义一个二维数组,存储多个学生的名字
	char strArr[5][100] = {
		"sadf",
		"eg",
		"hbt",
		"na",
		"cse",
	};
	//遍历二维数组
	for (int i = 0; i < 5; i++)
	{
		char* str = strArr[i];
		printf("%s\n", str);
	};
	//第二种方式
	//把五个字符串的指针,放入一个数组中
	// 指针数组
	const char* strArr2[5] =
	{
		"sadf",
		"eg",
		"hbt",
		"na",
		"cse",
	};
	//遍历
	for (int i = 0; i < 5; i++)
	{
		const char* str = strArr2[i];
		printf("%s\n", str);
	}
	return 0;
}

结果

sadf
eg
hbt
na
cse
sadf
eg
hbt
na
cse

在书写代码时发生C++错误:“const char *“ 类型的值不能用于初始化 “char *“ 类型的实体

错误代码如下

//把五个字符串的指针,放入一个数组中
	// 指针数组
	char* strArr2[5] =
	{
		"sadf",
		"eg",
		"hbt",
		"na",
		"cse",
	};
	//遍历
	for (int i = 0; i < 5; i++)
	{
		char* str = strArr2[i];
		printf("%s\n", str);
	}

类型不匹配:字符串字面值"sadf",“eg”,“hbt”,“na”,"cse"的类型是 const char*,即指向常量字符的指针(const char*),而 char* strArr2[5] 定义了存储指针数组,每个元素应该是 char* 类型的指针(指向字符的指针)。所以需要在前面加上const表示常量。

常用函数
名字作用
strlen获取字符串长度
strcat拼接两个字符串
strcpy复制字符串
strcmp比较两个字符串
strwr将字符串变成小写
strupr将字符串变成大写

结构体

定义与遍历

格式:

struct 变量名
{
	类型 变量名;
};
#include<stdio.h>

struct Student
{
	char name[100];
	int age;
};

int main()
{
	//定义三个学生,同时进行赋值
	struct Student stu1 = { "zhang",19 }; 
	struct Student stu2 = { "wang",20 };
	struct Student stu3 = { "zheng",18 };
	//存入数组中
	struct Student stuArr3[3] = { stu1,stu2,stu3 };
	//遍历数组得到每一个元素
	for (int i = 0; i < 3; i++)
	{
		struct Student temp = stuArr3[i];
		printf("name:%s, age:%d\n", temp.name, temp.age);
	}

	return 0;
}

结果:

name:zhang, age:19
name:wang, age:20
name:zheng, age:18
别名
#include<stdio.h>

typedef struct 
{
	//别名
	char name[100];
	int age;
}m;

int main()
{
	//别名:定义两个学生,同时进行赋值
	m aaa = { "one",10 };
	m b = { "two",12 };
	//存入数组中
	m stuArr3[2] = { aaa, b};
	//遍历数组得到每一个元素
	for (int i = 0; i < 2; i++)
	{
		m temp = stuArr3[i];
		printf("name:%s, age:%d\n", temp.name, temp.age);
	}

	return 0;
}
结构体作为函数参数
#include<stdio.h>
#include<string.h>

typedef struct Student
{
	//别名
	char name[100];
	int age;
}m;
void method(m st);
void method2(m* p);
int main()
{
	//定义一个学生
	m stu;
	//赋值
	strcpy(stu.name, "aaa");
	stu.age = 0;
	//输出
	printf("初始数据:%s,%d\n", stu.name, stu.age);//aaa,0
	//
	method(stu);
	printf("method修改后数据:%s,%d\n", stu.name, stu.age);//aaa,0

	method2(&stu);
	printf("method2修改后数据:%s,%d\n", stu.name, stu.age);//aaa,0
	//细节:
	// 如果函数中写的是结构体类型的变量,相当于定义一个新的变量
	//此时把main函数中stu中的数据,传递给了method函数,并把stu中的数据赋值给新的变量st
	//我们在函数中,仅仅修改st中的值,对于main函数中的stu的值没有修改
	//所以不定义新的变量而传递stu的地址
	return 0;
}

void method(m st)
{
	printf("main初始数据:%s,%d\n", st.name, st.age);//aaa,0
	//修改
	printf("请输入要修改的学生名字\n");
	scanf("%s", st.name);
	printf("请输入要的修改学生年龄\n");
	scanf("%d", &(st.age));

	printf("method修改数据:%s,%d\n", st.name, st.age);//输入的
}
void method2(m* p)
{
	printf("main初始数据:%s,%d\n", (*p).name, (*p).age);//aaa,0
	//修改
	printf("请输入要修改的学生名字\n");
	scanf("%s", (*p).name);
	printf("请输入要的修改学生年龄\n");
	scanf("%d", &((*p).age));

	printf("method2修改数据:%s,%d\n", (*p).name, (*p).age);
}
初始数据:aaa,0
main初始数据:aaa,0
请输入要修改的学生名字
asdf
请输入要的修改学生年龄
12
method修改数据:asdf,12
method修改后数据:aaa,0
main初始数据:aaa,0
请输入要修改的学生名字
asdf
请输入要的修改学生年龄
32
method2修改数据:asdf,32
method2修改后数据:asdf,32
结构体嵌套
#include<stdio.h>
#include<string.h>

struct message
{
	char phone[12];
	char mail[100];
};

struct Student
{
	
	char name[100];
	int age;
	char gender;
	double height;
	struct message msg;
};

int main()
{
	//
	struct Student stu;
	strcpy(stu.name, "zhang");
	stu.age = 21;
	stu.gender = 'M';
	stu.height = 1.81;

	strcpy(stu.msg.phone, "12312412424");
	strcpy(stu.msg.mail, "12333245@qq.com");
	//批量赋值
	struct Student stu2 = { "li",32,'F',1.65,{"12132435424","33245@qq.com"} };

	printf("信息:\n");
	printf("name:%s\n", stu.name);
	printf("age:%d\n", stu.age);
	printf("gender:%c\n", stu.gender);
	printf("height:%.2lf\n", stu.height);

	printf("phone:%s\n", stu.msg.phone);
	printf("mail:%s\n", stu.msg.mail);


	printf("2信息:\n");
	printf("name:%s\n", stu2.name);
	printf("age:%d\n", stu2.age);
	printf("gender:%c\n", stu2.gender);
	printf("height:%.2lf\n", stu2.height);

	printf("phone:%s\n", stu2.msg.phone);
	printf("mail:%s\n", stu2.msg.mail);
	return 0;
}

结果:

信息:
name:zhang
age:21
gender:M
height:1.81
phone:12312412424
mail:12333245@qq.com
2信息:
name:li
age:32
gender:F
height:1.65
phone:12132435424
mail:33245@qq.com
结构体内存对齐

内存对齐:
不管是结构体,还是普通的变量都存在内存对齐
规则:
只能放在自己类型整数倍的内存地址上简单理解:
内存地址/占用字节=结果可以整除
举例;
int存放的位置:内存地址—定能被4整除
long long存放的位置:内存地址—定能被8整除;double存放的位置:内存地址—定能被8整除
结构体的内存对齐:
结构体在上面的基础上又多了一条,结构体的总大小,是最大类型的整数倍(用来确定最后一个数据补位的情况)
切记! !
对齐的时候会补空白字节,但是不会改变原本字节的大小char补位之后,本身还是1个字节
心得:
我们会把小的数据类型,写在最上面,大的数据类型,写在最下面(节约空间)

共用体

定义
#include<stdio.h>

union moneytype
{
	//共用体相当于定义一个自己的变量,可以包含任何变量类型。
	int moneyi;
	double moneyd;
	char moneystr[100];
};

int main()
{
	//利用共同体定义钱的变量
	union moneytype money;
	//赋值
	//整数只能赋给moneyi
	//每次只能赋一个值
	money.moneyi = 99999;
	printf("%d\n", money.moneyi);

	return 0;
}
共用体的特点
  • 共用体,也叫联合体,共同体
  • 所有的变量都使用同一个内存空间
  • 所占的内存大小=最大成员的长度(也受内存对齐影响)
  • 每次只能给一个变量进行赋值,因为第二次赋值时会覆盖原有的数据
#include<stdio.h>

union moneytype
{
	//共用体相当于定义一个自己的变量,可以包含任何变量类型。
	int moneyi;
	double moneyd;
	char moneystr[100];
};

int main()
{
	//利用共同体定义钱的变量
	union moneytype money;
	//共用体的特点:
	//	1.共用体,也叫联合体,共同体
	//	2.所有的变量都使用同一个内存空间
	//	3.每次只能给一个变量进行赋值,因为第二次赋值时会覆盖原有的数据
	//	4.所占的内存大小 = 最大成员的长度(也受内存对齐影响)
	//	细节:以最大的单个成员的长度为准总大小一定是最大单个成员的整数倍
		//1.利用共同体定义钱的变量
	//2.获取内存地址
	printf("%p\n", &(money.moneyi));
	printf("%p\n", &(money.moneyd));
	printf("%p\n", &(money.moneystr));

	printf("%zu\n", sizeof(money.moneyi));
	printf("%zu\n", sizeof(money.moneyd)); 
	printf("%zu\n", sizeof(money.moneystr));
	printf("%zu\n", sizeof(money));//104(后面会补4个空白字节)
	money.moneyi = 99;
	money.moneyd = 1.23;
	printf("%1f\n", money. moneyd);
	printf("%d\n", money.moneyi);//结果是错误
	return 0;
}
结构体和共用体的区别
  • 结构体:一种事物中包含多个属性
  • 共用体:一个属性有多种类型

存储方式:

  • 结构体:各存各的
  • 共用体:存一起,多次存会覆盖

内存占用:

  • 结构体:各个变量的总和(受内存对齐影响)
  • 共用体:最大类型(受内存对齐影响)

动态内存分配

函数
函数名全部单词作用
mallocmemory allocation申请空间(连续
calloccontiguous allocation申请空间+数据初始化
reallocre-allocation修改空间大小
freefree释放空间
#include<stdio.h>
#include<stdlib.h>

int main()
{
	int* p = (int*)malloc(100 * sizeof(int));
	printf("%p\n", p);
	for (int i = 0; i < 10; i++)
	{
		//第一种赋值,用地址
		//*(p + i) = (1 + i) * 10;
		//第二种赋值,用数组
		p[i] = (i + 1) * 10;
	}
	
	for (int i = 0; i < 10; i++)
	{
		//
		//printf("%d\n", *(p + i));
		//
		printf("%d\n", p[i]);
	}
	return 0;
};
动态内存分配的小细节
  1. malloc 创建空间的单位是字节
  2. malloc 返回的是void类型的指针,没有步长的概念,也无法获取空间中的数据,需要强转
  3. malloc 返回的仅仅是首地址,没有总大小,最好定义一个变量记录总大小
  4. mallod 申请的空间不会自动消失,如果不能正确释放,会导致内存泄露
  5. malloc 申请的空间过多,会产生虚拟内存
  6. malloc 申请的空间没有初始化值,需要先赋值才能使用
  7. free释放完空间之后,空间中数据叫做脏数据,可能被清空,可能被修改为其他值
  8. calloc 就是在malloc 的基础上多一个初始化的动作
  9. realloc 修改之后的空间,地址值有可能发生变化,也有可能不会改变,但是原本的数据不会丢失
  10. realloc 修改之后,无需释放原来的空间,函数底层会进行处理
#include<stdio.h>
#include<stdlib.h>

int main()
{

	//5,ma11oc申请的空间过多,会产生虚拟内存
	//虚拟内存:
	//虚拟:假的
	// 当申请的空间过多,因为每一个内存空间不会在刚申请的时候就立马使用
	//所以c语言并不会立马就在内存中去开辟空间,而是什么时候存储数据了,才会真正的分配空间
	//目的:为了提高内存的使用效率
	表示单词申请空间的字节大小(1G)
	int number = 1024 * 1024 * 1024;
	//利用循环不断地申请空间
	//malloc 申请空间(连续)
	//如果申请空间成功,返回这个空间的首地址
	//如果申请空间失败,返回NULL
	int count = 0;
	while (1)
	{
		int* p = (int*)malloc(number);
			count++;
		if (p == NULL)
		{
			printf("申请失败");
				break;
		}
		printf("内存%d申请成功%p\n", count, p);
	}
	return 0;
};
  • 28
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值