C语言中-const,volatile-struct,union-enum,typedef,define

一、const和volatile分析

1、const修饰变量

# 在c语言中const修饰的变量时只读的,其本质还是变量

# const修饰的变量会在内存占用空间

# 本质上const只对编译器有用,在运行时无用

原来const不是真的常量


2、const修饰数组

# 在C语言中const修饰的数组是只读的

#const修饰的数组空间不可以被改变

const int A[5] = {1, 2, 3, 4, 5}

int *p = (int *)A;

int i = 0;

for(i=0; i<5; i++)

{

p[i] = 5-i;//报错

}

3、const修饰指针

# const int *p; //p可变,p指向的内容不可变

# int const *p; //p可变,p指向的内容不可变

# int *const p; //p不可变,p指向的内容可变

# const int *const p;  //p不可变, p指向的内容不可变

口诀:左数右指

当const出现在*号左边时指针指向的数据为常量,当const出现在*后右边时指针本身为常量

const修饰函数参数和返回值

# const修饰函数参数表示在函数体内不希望改变参数的值

# const修饰函数返回值表示返回值不可改变,多用于返回指针的情形

const int * func()

{

static int count = 0;

count++

return &count;

}

深藏不漏的volatile

1、volatile可理解为“编译器警告指示字”

2、volatile用于告诉编译器必须每次去内存中去取变量值

3、volatile主要修饰可能被多个线程访问的变量

4、volatile也可以修饰可能被未知因数改变的变量


int obj = 10;

int a = 0;

int b = 0;


a = obj;

sleep(100);

b = obj;

以上代码,编译器在编译的时候发现obj没有被当成左值使用,因此会“聪明”的直接将obj替换成10,而把a和b都赋值为10.


课后思考

# const和volatile是否可以同时修饰一个变量?

# const volatile int i = 0; 这个时候i具有什么属性?编译器如果处理这个变量?

答:可以。这个时候i具有const和volatile的双重属性。i变量不可以在编译过程中被程序代码修改,同时编译器不得对i进行优化编译。

二、struct与union分析

1、空结构体占用多大内存?

#include <stdio.h>


struct D
{
};

int main()
{
struct D d1;
struct D d2;


printf("%d\n", sizeof(struct D));
printf("%d,%0x\n", sizeof(d1), &d1);
printf("%d,%0x\n", sizeof(d2), &d2);

return 0;
}

使用g++编译后结果为

1

1,22ff4f

1,22ff4e

使用gcc编译后运行结果为

0

0,22ff50

0,22ff50

可以看出使用gcc编译结果,不合理,因为d1和d2是相同的地址,而两个变量占有相同的地址这是不可思议的。

而g++似乎合理。除此之外,我们还可以看到,对于不同的编译器,它们往往对空的结构体有不同的定义,所以我们尽量不要定义空结构体,避免这个C语言的灰色地带。

由结构体产生柔性数组

1、柔性数组即数组大小待定的数组

2、C语言中结构体的最后一个元素可以是大小未知的数组

3、C语言中可以由结构体产生柔性数组

struct SoftArray

{

int len;

int array[];

};

柔性数组的实例分析:

#include <stdio.h>
#include <malloc.h>

typedef struct _soft_array
{
    int len;
    int array[];
}SoftArray;

int main()
{  
    int i = 0;
    SoftArray* sa = (SoftArray*)malloc(sizeof(SoftArray) + sizeof(int) * 10);
    
    sa->len = 10;
    
    for(i=0; i<sa->len; i++)
    {
        sa->array[i] = i + 1;
    }
    
    for(i=0; i<sa->len; i++)
    {
        printf("%d\n", sa->array[i]);   
    }
    
    free(sa);
    
    return 0;
}

柔性数组的使用:存储斐波拉次数列

思维导图:

代码示例:

#include <stdio.h>
#include <malloc.h>

typedef struct soft_array
{
	int len;
	int array[];
}Soft_Array;

Soft_Array * creat_soft_array(int size)
{
	Soft_Array *ret = NULL;
	
	if(size > 0)
	{
		ret = (Soft_Array*)malloc(sizeof(Soft_Array) + sizeof(int) * size);
		ret->len = size;	
	}
	return ret;	
}

void fac(Soft_Array *ret)
{
	if(ret != NULL)
	{
		int length = ret->len;
		if(1 == length)
		{
			ret->array[0] = 1;
		}
		else
		{
			ret->array[0] = 1;
			ret->array[1] = 1;
			if(2 < length)
			{
				for(int i=2; i<length; i++)
				ret->array[i] = ret->array[i-1] + ret->array[i-2];
			}
   		}	
	}	
}

int del_soft_array(Soft_Array *SA)
{
	free(SA);
}

int main()
{
	int size = 10;
	
	Soft_Array* ret = creat_soft_array(size);
	fac(ret);
	
	for(int i=0; i<size; i++)
	{
		printf("%d\n", ret->array[i]);
	}
	del_soft_array(ret);
	
	return 0;
}

运行结果:


union和struct的区别

1、struct中的每个域在内存中都独立分配空间

2、union只分配最大域的空间,所有域共享这个空间

struct A

{

int a;

int b;

int c;

};


union B

{

int a;

int b;

int c;

}


int main()

{

printf("%d\n", sizeof(struct A)); //12

printf("%d\n", sizeof(union B)); //4


return 0;

}

union的使用受系统大小端的影响

大端模式:低位字节放在高地址,高位字节放在低地址

小端模式:高位字节放在高地址,地位字节放在低地址

union C

{

int i;

char c;

};

union C c;

c.i  = 1;

printf("%d\n", c.c);//小端模式下,为1.(取一个字节低地址);

三、enum和typedef分析

1、enum是一种自定义类型,是真正意义的常量,等价于命名的常数,是字面常量。

2、enum默认常量在前一个值得基础上依次加1

3、enum类型的变量只能取定义时的离散值,不能是浮点类型。可以显示定义,或默认从0依次累加

#include <stdio.h>
#include <malloc.h>

enum color
{
GREEN,
RED = 5,
BLUE
};

int main()
{
printf("%d\n", GREEN);
printf("%d\n", RED);
printf("%d\n", BLUE);

return 0;
}

运行结果

0

5

6

枚举类型和#define的区别

4、#define宏常量只是简单的进行值替换,枚举常量是真正意义上的常量

5、#define宏常量无法被调试,枚举常量可以。因为define定义的常量会在编译前进行预处理,即进行值替换,所以不可以进行调试

6、#define宏常量无类型信息,枚举常量是一种特定类型的常量

typedef的意义

7、typedef用于给一个已经存在的数据类型重命名

8、typedef并没有产生新的类型

9、typedef重定义的类型不能进行unsigned和signed扩展

typedef和#define的区别

10、typedef是给已有类型取别名

11、#define为简单的字符串替换,无别名的概念

typedef char * PCHAR;

PCHAR p1, p2;


#define PCHAR char *

PCHAR p3, p4

上面的定义中p1、p2、p3都是char *类型的,但p4只是char类型。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值