c语言小知识

标签:c语言小知识

c语言小知识

1.取出一个数据中的第三个字节:
  • 列表项

unsigned char tmp = 0x70060012; //其中06为我们要取的第三个字节

tmp = (tmp >> 16) & 0xff;
2.如何优雅的设置bit

我们见多了例如这样的代码:

mask |= (1<<7)|(1<<23);

一会左移一会右移,是不是很简单呢。但是如果不借助datasheet你能看出来他是什么功能吗,恐怕猜也猜不出来吧,是不是很糟糕
我们可以全局一个这样的函数:
set_reg_bit(uint reg,char offset)
clear_reg_bit(uint reg,char offset)
再配上注释是不是很好呢,当然这只是个编程风格的事

对0X18000488寄存器的[17]bit进行置位操作:
置1操作:

__REG32(0X18000488) |= 1 << 17;

置0操作:

__REG32(0X18000488) &= ~(1 << 17);
3.结构体强转

结构体间的类型强转
总结:结构体实质上来说是一段约定好长度和存放数据类型的内存,结构体间的强转并不改变总体内存的分布,只是改变内部存放数据的类型
例如:

struct A{
    int a;
    char b;
    char c;
};
struvt B{
    char a;
    int b;
};
A * x1;
b * x2;
x1 = ((A*)x2);

这样的操作以后会将x2中的内存分布中的数据赋值给x1,,强转会将x2中的内存按照x1的格式进行组织,然后拷贝给x1,但由于两个结构体的内存分配不一样,所以此时的x1中的成员数值是未知的即垃圾值。

struct B{
    int a;
    char b;
};

此时再操作x1 = ((A*)x2);由于x2中的内存格式和x2是一致的,故x1将可以得到x2的数据

4.指针和变量
4.1
static INT32 test_hw_initialize(struct test_device *dev, struct dev_cfg *dev_cfg)
{

    priv_data_t *priv = dev->priv;
  
	if(priv->dev_init)
	{
		return SUCCESS;
	}
	priv->nim_init = TRUE;
    return SUCCESS;
}

当时的疑问:传进一个指针,在本地新建一个指针变量: sony_demod_t *priv
然后用新变量指向传进指针所指向的内存空间: sony_demod_t *priv = dev->priv;
然后通过本地指针变量进行操作:priv->nim_init = TRUE;
当函数执行完以后:priv->nim_init = TRUE这个修改的值会跟着一起消失吗?
答:不会消失,一句话概括,本地的指针变量会消失,但指针指向的空间中的修改的值不会消失。

4.2 指针传参,一般用于修改指针所指向的地址空间中的内容
void test(char* str_p)
{
      strcpy(str_p,"this is a test");
}
int main(void)
{
	  char * str = malloc(100);
      test(str);
      printf("str=%s\n",str);
      return 0;
}
void test(char* str_p)
{
      strcpy(str_p,"this is a test");
}
int main(void)

{
      char str[100] = {0};
      test(str);
      printf("str=%s\n",str);
      return 0;
}

这两种有个共同点,指针指向的地址空间是在外部开辟的,内部只是传入指针去改变指针指向的地址空间中的内容。两中场景虽然传入test的都是指针值的拷贝,原指针和拷贝指针虽然指针变量的地址不一样(如下所示),但是指针的值都是一样的,所以指向的都是同一片地址空间。虽然test执行完后,系统释放的是拷贝值str_p,该指针指向的地址空间并不会被释放

&str = 0x01    str = 0x03
&str_p = 0x02  str_p = 0x03  因为str,str_p指针值一致,所以指向的地址空间也是统一片地址空间,

C语言强化指针高级应用学习心得
C提高:指针强化
C语言强化——指针

4.3 使用指针传递新的地址空间

4.3.1 传参方式

void test(char* str_p)//这里传入的str_p只是str的一个拷贝,只不过str和str_p 2个指针的值是相同的,指向同一片内存空间
{
	char new_str[100];
	str_p = new_str; //这里相当于修改了str_p原来的值,使起指向方向都变了,程序执行完后,str_p本身会被释放,
	而且其指向的是一片栈空间(new_str),所以栈空间也会被释放
    strcpy(str_p,"this is a test");
}
int main(void)
{
	  char * str = NULL;
      test(str);//str并没有将新空间传递回来
      printf("str=%s\n",str);
      return 0;
}

所以,这里需要二级指针来完成这个事情。

void test(char ** str_p)//这里传入的str_p是二级指针,只是二级指针&str的一个拷贝,所以str_p和&str这2个指针的值是相同的,
指向同一片内存空间,即他们指向了同一个一级指针str。但是str_p和&str的地址是三级指针,是不同的
{
	char *new_str = malloc(100);
	*str_p = new_str; //让*str_p指向新地址空间即malloc出的空间,因为str_p和&str这2个指针的值是相同的,
	指向同一片内存空间,即他们指向了同一个一级指针str,所以str的指向也发生了改变,也指向了mallco的空间。
	程序执行完后,str_p本身会被释放,但是*str_p也就是str指向的malloc区在堆区,不会被释放。
    strcpy(*str_p,"this is a test");
}
int main(void)
{
	  char * str = NULL;
      test(&str);//注意这里传入的是指针的地址
      printf("str=%s\n",str);
      return 0;
}

4.3.1 返回值方式

char* test()
{
      char * str = malloc(20);
      strcpy(str,"hello world");
      return str;//返回的是malloc的地址,test执行完后,他的本身的值被传递给了外部的pStr,随后str本身会被系统是释放。
      因为值本身是一个指针,所以外部的pStr指向的地址空间也就被改变了,不再是NULL
}
int main(void)
{
      char* pStr = NULL;
      pStr = test();
      puts(pStr);
      free(pStr);
      return 0;
}
5.extern 的小风波

下面为一个.h文件中的函数声明:

extern INT32 dev_attach_ext(char *name, PCOFDM_TUNER_CONFIG_API pConfig);
extern INT32 i2c_scb_write(UINT32 id, UINT8 slv_addr, UINT8 *data, int len);
extern INT32 i2c_scb_read(UINT32 id,UINT8 slv_addr, UINT8 *data, int len);

Q:当时很纳闷,既然已经在.h中声明了,要在其他文件中访问这些函数只需要include这些.h文件即可,
怎么又要使用extern来修饰一下呢?
R:对于在.h中extern函数的做法,这只不过是代码风格的做法,因为有些人在声明一个函数时习惯于 extern return_value func()
所以在.h中的也会这样子做,但一般如果你通过添加.h的方式去访问另一个文件中的函数,一般这个被inlcude的.h是不需要使用extern函数的
eg:
A.c A.h
B.c B.h
如果B.c中要使用A.c中的函数有两种做法:
1:include<A.h>
2: B.c中extern这个函数而不需要#include.h文件
NOTE:使用static修饰的函数是不能extern和加在.h
1:extern是函数的原型,可带可不带
对于在.h中extern函数的做法,这只不过是代码风格的做法,因为有些人在声明一个函数时习惯于 extern return_value func();extern是函数的原型,可带可不带;
所以在.h中的也会这样子做,但一般如果你通过添加.h的方式去访问另一个文件中的函数,一般这个被inlcude的.h是不需要使用extern函数的

6.#if #elif #else

(一)条件编译方法
 条件编译是通过预编译指令来实现的,主要方法有:
1、#if, #elif, #else, #endif

#if 条件 1
 代码段 1
#elif 条件 2
   代码段 2
...
#elif 条件 n
 代码段 n
#else
 代码段 n+1
#endif
#ifdef macro_name
代码段 1
#elif defined other_macro_name
#else
代码段 2
#endif

NOTE:最好使用#ifdef 来进行判断,因为#if只是会判断是否为0或1,但是#ifdef却会判断你的宏是否定义,更加严格和规范
测试过,使用宏来隔开的语句是不参与编译的,而且编译器报错是会绕过这段语句的

#undef就是取消一个宏的定义,之后这个宏所定义的就无效;

7.volatile的作用

volatile是为了防止编译器对变量进行优化

8.小心翼翼的数组

案例一:
定义一个静态数组为:
int a[100] = 0;
但是在后面赋值的时候

for(i =0;i<200;i++)
{
	a[i] = x;
}
priv-function();

可以看到,只有100个int型的数组,在100个成员赋值完成以后,后面的100个int型空间也进行了初始化,这就摧毁了100个int空间后的这些数据;
这就导致程序有问题.priv-function()这个函数指针就是无效的,也就是个野指针,会导致内核奔溃,即发生oops
案例二:

UINT8 wLen=0;	
	wLen = size+1;
	UINT8 Cmd[wLen];
	Cmd[0] = subAddress;
	//Cmd[1] = RegData;

上面我们看到,数组的大小是由一个变量来控制的,这种写法不报错,程序也能正常运行,有warning;如果我们要清掉warning就只能写成下面这样:

UINT8 wLen=0;	
	UINT8 Cmd[wLen];
	wLen = size+1;
	Cmd[0] = subAddress;
	//Cmd[1] = RegData;

这样的话warning是没有了,但我们的程序完蛋了,我们的数组不存在了,长度为0;
UINT8 wLen=0; UINT8 Cmd[wLen];
静态数组的大小一般要用一个常量来控制,不能用变量。如果遇到动态数组,就要使用kmalloc的方式了,如下:

uint8_t * Cmd = NULL;//定义一个空指针
	wLen = size+1;
	Cmd = (uint8_t *)kmalloc(wLen, GFP_KERNEL);//根据变量动态的去分配
	if(NULL == Cmd)  //判断是否分配成功
	{
		CXD2872_PRINTF(NIM_LOG_DBG,"\n Kmalloc fail \n");
		return SONY_RESULT_ERROR_OTHER;
	}
	//UINT8 Cmd[wLen];
	Cmd[0] = subAddress;

所以不要小看任何一个数组,它可能会让你在出错的时候很难定位,保持一个良好的编程习惯很重要
另外:不要忽视任何一个warning

9.你真的会使用if吗

通常我们的判断会是:

1.
if(i == 10)
{

}
2.
if(18 == i)
{

}

我们一般习惯的是第一种,但是第二种其实才是最好的。
对于if(i == 10)我们很容易写成 if(i = 10),此时,编译器不会报错,且会有以下结果:第一,i会被赋值为10.第二这个判断永远成立即永远为真。
对于if(10 == i),如果写成if(10 = i),编译器会报错提醒我们。
所以使用if(i == 10)是一种不规范的写法。

10.if中的所谓真

if判断时,条件为真执行,所谓的为真即条件不为0或者条件成立(1<2)

11.低级错误
struct CONFIG_DATA *config_data = &cfg.config_data;
struct CONFIG_EXT  *tuner_config_ext = &cfg.tuner_config_ext;
struct CONFIG_DATA config_data = cfg.config_data;
struct CONFIG_EXT  tuner_config_ext = cfg.tuner_config_ext;

3

tuner_config_ext.i2c_type_id       = buff[0];
tuner_config_ext.base_addr = buff[1];

经过步骤1,3或者2,3以后,哪个步骤后的&cfg.config_data中的i2c_type_id和base_addr会变?
真是低级错误

12.由互斥锁联想到程序设计

今天碰到driver加互斥锁的问题,系统是类似ucos的系统,不是linux系统,driver设计的时候有通过ioct与上层交换数据,也有与一个attach函数通过一个dev指针调用dev指针中
的函数指针去调用这些函数A,B,C,D,E…。也就是说driver与上层交互的有两个接口,而且两个接口中调用的函数A,B,C,D,CE…都是重叠的,而上层对这两个接口都有使用。
因为有多个export到上层的接口,其中还有一个是直接调用的函数指针。所以不能直接在export的接口中加锁,这样的话只能在他们调用的函数A,B,C,D,E…中加锁.但是又出现一个问题
这些函数中有的是互相调用的,同时被调用的和调用的又都是上层接口调用的,例如,A中会调用到B,C函数,同时,A,B,C函数又都是被上层函数直接调用的这样使用一个互斥锁就会
导致死锁,但是使用多个不同的互斥锁又会使程序很复杂,(本身调用已经很繁杂了,若处理不好就会死锁),所以这种情况最好的方式是在调用的地方加锁即hal层。
加锁的原则是从用户态到driver层层加锁最好,但是上面的情况是driver本身设计的问题导致的加锁困难。
所以在设计driver程序时,提供给上层的接口一定要使用一个统一的接口去调用,即export上层的接口最好是一个或者避免是使用一个结构体中的函数指针去调用,这样我们加锁的时候
就可以只在这些export的接口头尾加锁和解锁,很方便,管理起来也很轻松。

一般的当我们对一个函数进行加锁时,但是这个函数中有太多的return 操作如下时,我们就需要在每一个return时进行解锁,这样就会很繁复;

int func_a(char *p)
{
	mutex_lock(&mutex);
	if()
	{
		mutex_unlock(&core_lock);
		return xxx
	}
	if()
	{
		mutex_unlock(&core_lock);
		return xxx
	}
	.......
}

此时我们可以这样做,重新封装一个函数,这样只需头尾加锁和解锁,很方便,代码也不是很乱。

int func_a_mutex(char *p)
{
	int ret = 0;
	mutex_lock(&mutex);
	ret = func_a(p);
  mutex_unlock(&core_lock);
	return ret;
}
13.不刷屏的打印

只在当前行打印,类似于倒计时这样的效果,此时可以给打印函数后面加上\r
printf(" the nu is %d\r",count);

14.while(1)中使用sleep时打印不输出

在执行这段代码时,发现程序没有打印输出

	int count = 0;
	while(count<1024)
	{
		printf("  the nu is %d\r",count);\\ \r表示打印完后回到最左边
		//delay_ms(10000);
		
		sleep(1);
		count++;
	}
	printf("\n");
	return 0;

将\r改为\n后打印输出,但是违背程序功能,我们希望她每次都在同一行刷新打印

int count = 0;
	while(count<1024)
	{
		printf("  the nu is %d\n",count);
		sleep(1);
		count++;
	}
	printf("\n");
	return 0;

后改为下面后,正常

	int count = 0;
	while(count<1024)
	{
		printf("  the nu is %d\r",count);\\ \r表示打印完后回到最左边
		sleep(1);
		fflush(stdout);
		count++;
	}
	printf("\n");
	return 0;

原因:printf有一buf,只有buf满或者遇到换行\n才会将缓冲区的数据打印出去,所以上述程序有这样一个现象:当count达到一个数值的时候就会打印一次
这其实与我们在while(1)中使用sleep导致程序卡住无关,是printf的原因;
上面我们使用\r当然不会打印出来。所以解决方法有两个:
方法一: 使用\n:遇\n打印
方法二:强制刷新缓冲区,即使用函数fflush刷新输出端口缓冲区

15.退格的快捷键

shift + table

16.gettimeofday函数的使用
struct timeval t_start,t_end,t_cost;
gettimeofday(&t_start,NULL); //first get time
gettimeofday(&t_end,NULL);
timersub(&t_end, &t_start, &t_cost);
lock_time = t_cost.tv_sec*1000 + t_cost.tv_usec/1000;

//这句很重要,一定要这么算,不然是算不出正确的时间差的
如果是个负值,就是数值溢出的问题,下面即可
lock_time = (double)t_cost.tv_sec*1000 + (double)t_cost.tv_usec/1000;

17.ioctl拿不到值

情况1.

int cner = 0;
int level = 0;
ioctl(dev->fd, GET_R_LEVEL,&level);
ioctl(dev->fd, GET_C_VALUE, &cner );

刚开始我一直拿不到值,很奇怪。最后看了代码才发现:driver中的传值方式是靠ioctl的返回值来传递的,不是靠ioctl中的参数往出带值的,就这样两个小时没有了
so,需要这样子搞

level = ioctl(dev->fd, GET_R_LEVEL,0);
cner = ioctl(dev->fd, GET_C_VALUE,0);

情况2:

static long dev_ioctl(struct file *file, unsigned int cmd, unsigned long parg)
{
 	switch (cmd)
	{
		case GET_R_LEVEL:
		{
			INT8 value = 0;
			INT32 tmp = 0;
			get_dbm(dev,&value);
			ret = &value;		
			printk("value 1= %d\n",value);
			break;
		}
	return ret;
	}
}

此时发现使用这样的调用:level = ioctl(dev->fd, GET_R_LEVEL,0);得到的值永远是-1.如果把底层上传的值强制为正直,ok,值可以上传,但是负值上层得到的永远是-1;
百思不得其解。
最近只有man ioctlle 一下
RETURN VALUE Usually, on success zero is returned. A few ioctl() requests use the return value as an output parameter and return a nonnegative value on success. On error, -1 is returned, and errno is set appropriately.
所以,这个ioctl函数只有当erro的时候才会返回-1.也就是说负数除了-1,不会返回其他的负数

所以,就只有老老实实的使用copy_to_user吧,或者传这个值的绝对值上去。不要使用ioctl的返回值去传值

18.函数传递数组的问题

犯了个低级错误

static INT32 get_segment_nu(struct nim_device *dev,UINT32 *three_layer)
{
	UINT32 tmp_array[3];
	xxxx
	tmp_array[0] = x;
   tmp_array[1] = xx;
	tmp_array[2] = xxx;
	three_layer = tmp_array;
}

这个函数本意是传递一个数组进来,然后将值通过传入的数组带出去。但是发现调用函数后这个值总是拿不到,刚开始看了一会觉得没道理,
最后发现问题出在这一句上

three_layer = tmp_array;

这是将一个局部数组的地址赋值给一个外部的指针,这样的做法是错误的,因为函数调用完毕后,函数的局部变量就会被释放掉,所以tmp_array这个地址也就没有值了,
three_layer指向的这个地址也就没有值了
所以修改:

static INT32 get_segment_nu(struct nim_device *dev,UINT32 *three_layer)
{
	xxxx
	three_layer[0] = x;
   three_layer[1] = xx;
	three_layer[2] = xxx;
}

问题发现后思考为什么会出现这个问题,本意是想使用一个局部变量先拿值,然后将传入的指针指向这个地址就可以,因为传入的数组地址不知道其成员有多少个,所以怕数组溢出

上面传值穿不出去的问题解决后,下面关注传传入的数组安全性问题,

static INT32 get_segment_nu(struct nim_device *dev,UINT32 *three_layer)

假设这种接口传入的数组成员个数是2,但我内部操作的时候堆在这个地址按照3个成员这样子操作,这样第三个成员的操做就会导致该地址偏移三个地址大小处出现问题,会导致意想不到的问题。
所以传入的数组大小必须是大于我内部操作这个数组的偏移长度,即内部操作的数组成员个数必须小于传入的数组大小

但是这样是一种寄希望于调用者和使用者都标准调用的美好愿景,我们必须做出改变:即传入数组的时候必须传入数组的长度

static INT32 get_segment_nu(struct nim_device *dev,UINT32 *three_layer,UINT32 array_len)
{
		if((three_layer == NULL) || (array_len < 3))
		{
			return ERR_FAILUE;
		}
}

总结:要么传递结构体,要么传递数组的同时传入数组的长度

19.多位数组的初始化
warning:missing braces around initializer

正确的初始化

R858_SectType R858_IMR_Data[R858_MAX_NUM][2][R842_IMR_POINT_NUM] = {{{{0}}}};
UINT8  R858_Fil_Cal_flag[R858_MAX_NUM][2][R858_STD_SIZE]={{{0}}};
20.指针函数和函数指针

http://www.cnblogs.com/code1527/p/3249027.html

容易混淆的两点:
1.指针函数是返回指针的函数,形式为:
类型名 *函数名(函数参数表列);

	int *pfun(int, int); 《==》int *(pfun(int, int)); 

2.函数指针
先申明一个函数指针 int (*PF)(int *, int); PF是一个函数指针变量
函数返回值为函数指针:
一般标准的清晰的做法是:

typedef  int (*PF)(int *,int);

此时PF为一个函数指针“类型”,然后用这个类型去做函数的返回值
PF fun(int);

21.不同.c文件中同名的全局变量

忽然想起一个问题:
A.c 和B.c两个文件,均定义了了一个全局变量

A.c : int data = 0;
B.c : int data = 0;

两个.c同属一个文件夹,该文件夹在同一工程中
按照教科书上的说法这样会报多重定义的错误,但是单独去编译这个文件夹,居然没有报错。让人纳闷,后面重新编译整个工程,报错了。
查看编译log发现:单独编译的时候应该只是检查下语法错误,生成.o
编译整个工程时,回去链接这些.o此时就会报错
有的编译器也不会报错,会拿强符号的值作为该全局变量的值

22.编译时将打印重定向到log文件

make xxx 2>&1 > xxx.log

23.标准的数值打印格式

一般打印数值时候我们不必严格遵循,比如UINT32 也可以使用%d来打印
但是如果使用不当的话会造成warning。虽然不是error但是也是不严谨的。
如果编译选项中加入了-error,则所有的warning都会被当作error来处理。

正常的打印格式汇总如下
-----------------------------------------------------------
If variable is of Type,		use printk format specifier:

		int			        %d or %x
		unsigned int		%u or %x
		long			    %ld or %lx
		unsigned long		%lu or %lx
		long long		    %lld or %llx
		unsigned long long	%llu or %llx
		size_t			    %zu or %zx
		ssize_t			    %zd or %zx
		s32			        %d or %x
		u32			        %u or %x
		s64			        %lld or %llx
		u64			        %llu or %llx

path:\linux-PDK2.0xxxxxx\Documentation\printk-formats.txt
字,字节,bit
字 word
字节 byte
位 bit
字长是指字的长度
1字=2字节(1 word = 2 byte)
1字节=8位(1 byte = 8bit)

一个字的字长为16
一个字节的字长是8

1byte就是1B
1KB=1024B = 1024byte

24,程序间互斥所联想到的

假设有有一个库,为common_base code,现在有三个driver会用到库中的函数,这样各个driver对于库中的函数的使用会有一个竟态。如何解决这个问题?使用锁;使用锁的时候要考虑下面的情况:
1:这个锁对于这三个driver来说必须是全局的。每个driver调用函数前先去获得这个锁,然后再去操做函数
2:这个库函数中供外部调用的这些函数必须是可重入的,即虽然函数接口做到互斥了,但是当一个driver调用后,另外一个driver调用的时候会使这个函数内部的数据发生变化而导致上一个driver的调用接口出现异常。这样就不是一个可重入的。可重入的函数必须对每一个调用者而言,其数据都是独立的,最好的方式是传入每个调用者的私有数据类型指针,函数只对传入的私有数据类型指针做处理。这样,对于每个调用和这个driver的函数而言,每次传入的都是属于自己的私有数据。这个函数被其他driver调用也不会对其他driver的数据造成影响。
3:所以,互斥接口必须做到可重入
4:既然是外部的独立于这3个driver的互斥锁,那么意味着这个锁不属于任何一个driver的私有数据,这就牵扯到谁去初始化这个锁?可以做一个标志,哪个driver先启动谁就去初始化这个锁,其他driver检测到这个标志以后就不再去初始化。也可以单独做一个module——init去初始化这个锁

25.关于堆和栈

函数本身是没有堆和栈的,堆和栈的概念是对与进程和线程而言的;
每个线程有自己的堆栈,

栈:栈用于调用函数的时候传递这个函数的参数,返回地址,存储局部变量,保存上下文。基于这个原理,不同的线程去访问同一个函数的时候,会
在自己的栈中去入栈这些数据,调用完成后出栈。所以对于局部变量,不同的线程是拷贝并且入栈了局部变量在各自的栈中,操作的也是不用的数据了。
所以两个线程访问同一个函数中的局部变量是不会共享和受影响的。

堆:用于存静态分配的(kmalloc)一片空间。堆内的数据不会被系统清除,只有使用者自己管理产生和取消使用
全局变量和静态变量不在堆中,虽然都不会被系统清除该地址上的数据,但是它不能动态的产生和取消。它在编译的时候被定义

26.多个线程访问同一个接口的问题

多个线程访问同一个接口的问题:这种一般牵扯到一个互斥的问题:

case1:
如果该接口是含有全局变量,有动态申请内存在(在堆中的数据),有静态的局部变量,有需要进程数据循环发送(进程传参数给线程)之类的操作,这类函数一定要加入互斥操作
case2:
该函数中只有局部变量,或者传入的只是一个指针
每个线程有自己的堆栈,栈用于调用函数的时候传递这个函数的参数,返回地址,存储局部变量,保存上下文。基于这个原理,不同的线程去访问同一个函数的时候,会
在自己的栈中去入栈这些数据,调用完成后出栈。所以对于局部变量,不同的线程是拷贝并且入栈了局部变量在各自的栈中,操作的也是不用的数据了。
所以两个线程访问同一个函数中的局部变量是不会共享和受影响的。

对这种情况,这种函数就是一个可重入的函数,是不需要加锁互斥的。
因为对于局部的变量,是存在于栈中的。每个线程访问这个函数的时候实际就是在访问这个函数的堆栈数据,而访问的线程会拷贝一份该函数的栈数据到自己的线程栈中进行操作。第二个线程访问时候又会做同样的拷贝工作,所以两个线程操作的局部变量在各自的栈空间,双方不受影响,调用结束后,执行出栈操作,栈数据消失。
而对入传入的指针实际是一个地址,既然是一个地址,只要保证两个线程传入的地址不是一个地址,那么函数执行的就是不同的地址上的数据,也是互不影响的,
所以也是一种可重入的操作,不用加锁互斥。

27.字符串操作详细说明

http://www.jb51.net/article/37410.htm
sscanf 的返回值是指读到的值的个数
sscanf(tmp, “0x%x”,&monitor_object_tmp)成功返回1
sscanf(tmp,“w %s 0x%x 0x%x”,buf,&reg_addr,&reg_data)成功返回3
注意其中的类型格式要于要填充的数据类型一致。

28.三目运算符/条件表达式

一般来讲条件表达式只能判断两个,但是如果是3个或者更多的时候怎么办呢,有办法,可以这么做:
常用的是这样:

(a == 0)? b : c

超过2个的这样做:

(a == 0) ? b : ((c == 0) ? d : e) 嵌套着来
eg:
i == 0 ? 99 : (i == -1 ? 11 : 22);
pr_info("\n[%s]:plp_id:%d;para->usage_type:%s\n",__FUNCTION__,para->plp_id, \
	(para->usage_type ==USAGE_TYPE_C) ? \
	"USAGE_TYPE_C" :((para->usage_type ==USAGE_TYPE_AUTO) ? \
	"USAGE_TYPE_AUTO" :((para->usage_type ==USAGE_TYPE_NEXT)? "USAGE_TYPE_NEXT":"error para!!")));
29.宏的使用
一:
#define TRACE_RETURN_ERROR(string,result) \

pr_err("\n[error exit] %s,result=%d,%s,%d\n",string,result,__FUNCTION__, __LINE__); \
return (result);\


二:
#define TRACE_RETURN_ERROR(string,result) \
	do{\
		pr_err("\n[error exit] %s,result=%d,%s,%d\n",string,result,__FUNCTION__, __LINE__); \
		return (result);\
		}while(0);

一中的写法是不对的,只会执行第一句,只有使用二做法才可以,不用do while(0)的话,使用{}框起来也可以。

30.函数返回值
30.1 返回数值
int test()
{
	int a=0;
	a = a+5;
	return a;
}
int main()
{
	int b = test();
}
30.2 返回数组
char* test()
{
      char str[] = "this is a test";
      return str;
}
int main(void)
{
      char* pStr = NULL;
      pStr = test();
      puts(pStr);
      return 0;
}

这种写法是错误的,test中返回的是数组的地址,数组存储在栈上,test执行完后,栈会被释放,地址上的值就没了。虽然地址可以返回没问题,但是地址上的data已经被清空了

30.3 返回malloc的指针
char* test()
{
      char * str = malloc(20);
      strcpy(str,"hello world");
      return str;
}
int main(void)
{
      char* pStr = NULL;
      pStr = test();
      puts(pStr);
      free(pStr);
      return 0;
}

malloc的空间在堆上,函数运行完后,该片地址上的数据还在,所以返回地址后,地址上的之不变,可以继续使用
问题:30.1中的例子返回的数值a,也是test的局部变量,test运行后a就被释放了,为何还能传递出来?
这是因为1.test执行后确实a所在的地址空间上的值确实就被释放了2.但是我们传出的是地址上的值/数据,即将地址上的数据重新赋值给了外部的变量,传完后a所在的地址空间上的值被销毁,但是a的值是被传递出来的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值