C语言总结——杂七杂八

1.&是位运算(按位与),同时也是取地址符。&&是逻辑运算,逻辑与。

2.*是乘法运算(a*b),也是指针运算符(*p)。

3.反斜杠\同时具有接续符和转义符的作用。

编译器会将反斜杠剔除,跟在反斜杠后面的字符自动解到前一行。在接续单词时,反斜杠之后不能有空格,反斜杠的下一行之前也不能有空格。接续符适合在定义宏代码块时使用。

当反斜杠作为接续符使用时可直接出现在程序中,当反斜杠作为转义符使用时需出现在字符或字符串中。

4.注释/**/。编译器会在编译过程删除注释,但不是简单的删除而是用空格代替 in/**/t a,报错编译器认为双引号括起来内容都是字符串,双斜杠也不例外。“/*……*/”型注释不能被嵌套。

5.#运算符用于在预编译期将宏参数转换为字符串

##运算符用于在预编译期粘连两个符号

6.格式化输入输出

%(flags)(width)(.prec)type
以中括号括起来的参数为选择性参数, 而%与type则是必要的。底下先介绍type的几种形式整数
%d 整数的参数会被转成一有符号的十进制数字
%u 整数的参数会被转成一无符号的十进制数字
%o 整数的参数会被转成一无符号的八进制数字
%x 整数的参数会被转成一无符号的十六进制数字, 并以小写abcdef表示
%X 整数的参数会被转成一无符号的十六进制数字, 并以大写ABCDEF表示浮点型数
%f double 型的参数会被转成十进制数字, 并取到小数点以下六位, 四舍五入。
%e double型的参数以指数形式打印, 有一个数字会在小数点前, 六位数字在小数点后, 而在指数部分会以小写的e来表示。
%E 与%e作用相同, 唯一区别是指数部分将以大写的E 来表示。
%g double 型的参数会自动选择以%f 或%e 的格式来打印, 其标准是根据欲打印的数值及所设置的有效位数来决定。
%G 与%g 作用相同, 唯一区别在以指数形态打印时会选择%E 格式。
%c 整型数的参数会被转成unsigned char型打印出。
%s 指向字符串的参数会被逐字输出, 直到出现NULL字符为止
%p 如果是参数是"void *"型指针则使用十六进制格式显示。
prec 有几种情况
1). 正整数的最小位数。
2).在浮点型数中代表小数位数
3).在%g 格式代表有效位数的最大值。
4).在%s格式代表字符串的最大长度。
5).若为×符号则代表下个参数值为最大长度。
width为参数的最小长度, 若此栏并非数值, 而是*符号, 则表示以下一个参数当做参数长度,例:sscanf(buf, "%*s%s", dns);
flags 有下列几种情况
+ 一般在打印负数时, printf()会加印一个负号, 整数则不加任何负号。此旗标会使得在打印正数前多一个正号(+)。
# 此旗标会根据其后转换字符的不同而有不同含义。当在类型为o 之前(如%#o), 则会在打印八进制数值前多印一个o。
而在类型为x 之前(%#x)则会在打印十六进制数前多印’0x’, 在型态为e、E、f、g或G 之前则会强迫数值打印小数点。在类型为g 或G之前时则同时保留小数点及小数位数末尾的零。
0当有指定参数时, 无数字的参数将补上0。默认是关闭此旗标, 所以一般会打印出空白字符。

%10d -- 输出整型,十进制,占10位。 long int, short int 都可以用
%08x -- 按16进制无符号输出,小写,共8 位,不足8位左边添0
%20u -- 按十进制无符号数输出,占20位
%lf--double
h代表输入短整型数据

7.要对绝对地址0x100000赋值,我们可以用(unsigned int*)0x100000 = 1234;  那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?

*((void (*)( ))0x100000 ) ( );
首先要将0x100000强制转换成函数指针,即:
(void (*)())0x100000
然后再调用它:
*((void (*)())0x100000)();
用typedef可以看得更直观些:
typedef void(*)() voidFuncPtr;
*((voidFuncPtr)0x100000)();

8.短路规则:

||从左向右开始计算,当遇到为真的条件时停止计算,整个表达式为真;所有条件为假时表达式才为假。
&&从左向右开始计算,当遇到为假的条件时停止计算,整个表达式为假;所有条件为真时表达式才为真。

9.C语言隐式类型转换

算术运算式中,低类型转换为高类型
赋值表达式中,表达式的值转换为左边变量的类型
函数调用时,实参转换为形参的类型
函数返回值,return表达式转换为返回值类型

void foo(void)
{
    unsigned int a = 6; 
    int b = -20; 
    (a+b > 6) puts("> 6") : puts("<= 6"); 
}

10.指针和数组的对比

 
  

数组声明时编译器自动分配一片连续内存空间,指针声明时只分配了用于容纳指针的4字节空间

在作为函数参数时,数组参数和指针参数等价

数组名在多数情况可以看做常量指针,其值不能改变

指针的本质是变量,保存的值被看做内存中的地址

11.各种进制之间的转换

O<==>B(8<==>2)

二进制数与八进制数之间的转换 转换方法是:以小数点为界,分别向左右每三位二进制数合成一位八进制数,或每一位八进制数展成三位二进制数,不足三位者补0。例如: (423.45)8=(100 010 011.100 101)2 (1001001.1101)2=(001 001 001.110 100)2=(111.64)8

D<==>B(10<==>2)

整数和小数分别转换。整数除以2,商继续除以2,得到0为止,将余数逆序排列。

22 / 2 11 余0

11/2 5 余 1

5 /2 2 余 1

2 /2 1 余 0

1 /2 0 余 1

所以22的二进制是10110小数乘以2,取整,小数部分继续乘以2,取整,得到小数部分0为止,将整数顺序排列。0.8125x2=1.625 取整1,小数部分是0.6250.625x2=1.25 取整1,小数部分是0.250.25x2=0.5 取整0,小数部分是0.50.5x2=1.0 取整1,小数部分是0,结束所以0.8125的二进制是0.1101十进制22.8125等于二进制10110.1101

H<==>B(16<==>2)

转换方法:以小数点为界,分别向左右每四位二进制合成一位十六进制数,或每一位十六进制数展成四位二进制数,不足四位者补0。例如: (ABCD.EF)16=(1010 1011 1100 1101.1110 1111)2 (101101101001011.01101)2=(0101 1011 0100 1011.0110 1000)2=(5B4B.68)16

12.测试大小端(Little-endian和Big-endian模式)

32bit宽的数0x12345678

大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;

内存地址 存放内容 
0x4000 0x12 
0x4001 0x34 
0x4002 0x56 
0x4003 0x78 
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。

内存地址 存放内容 
0x4000 0x78 
0x4001 0x56 
0x4002 0x34 
0x4003 0x12
union xxx{
	int a;
	char b;
}kkk;

int checkCPU()
{
	kkk.a=12345678;
	return kkk.b;
}

void main()
{
	printf("%d\n",checkCPU());//78
}

13.传值和传址

#include 
#include 
   
void getmemory(char *p)
{ 
    p=(char *) malloc(100);
    strcpy(p,"hello world");
} 
   
int main( )
{
    char *str=NULL;
    getmemory(str);
    printf("%s\n",str);
    free(str);
    return 0;
}
程序崩溃,getmemory中的malloc 不能返回动态内存,free()对str操作很危险,getmemory中p是形参,是一个指针变量,getmemory(str)调用后,传入的是指针变量保存的对象地址,p=(char *) malloc(100)实际上是把申请的动态内存空间的首地址付给p指向的地址(即str指向的地址null)这个是错误的。应该修改成指向指针的指针 void getmemory(char **p),这样malloc返回的地址付给*p(即str变量本身)。正确的应该改为如下:
#include 
#include 
void getmemory(char **p)
{ 
    *p=(char *) malloc(100);
    strcpy(*p,"world");
}
int main( )
{
    char *str=NULL;
    getmemory(&str);
    printf("%s\n",str);
    free(str);
    return 0;
}

14.数组的地址

void main()
{
	int a[5]={1,2,3,4,5};
	int *ptr=(int *)(&a+1);
	printf("%d,%d",*(a+1),*(ptr-1));
}

输出:2,5
*(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5
&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)
int *ptr=(int *)(&a+1);
则ptr实际是&(a[5]),也就是a+5
原因如下:
&a是数组指针,其类型为 int (*)[5]。而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同。a是长度为5的int数组指针,所以要加 5*sizeof(int),所以ptr实际是a[5],但是prt与(&a+1)类型是不一样的(这点很重要),所以prt-1只会减去sizeof(int*)
a,&a的地址是一样的,但意思不一样
a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,
a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].

15.强制类型转换

int main()    
{    
	int a[5] = { 1, 2, 3, 4, 5 }; 
	int *p1  = (int*)(&a + 1);    
	int *p2  = (int *)((int)a + 1);
	printf("0x%x, 0x%x\n", p1[-1], *p2);  
	return 0;    
} 
变量a的内存地址为:0x0034fc50 问printf输出值是多少
首先分析p1
&a为数组指针,类型是"int (*)[5]",数组指针是指向数组首元素的地址的指针,&a的内存地址为:0x0034fc50,&a + 1指针加一,向后移动一个元素,这里的元素类型是"int (*)[5]",其空间大小为20字节,因此,&a + 1的内存地址为:0x0034fc64(0x0034fc50 + 0x14),p1的内存地址为:0x0034fc64 ,p1[-1]数组下标为负数,向前移动一个元素(注:数组越界在编译时与运行时都不会报错,除非操作非法内存),p1[-1]元素所在的地址为:0x0034fc60,因此,可知p1[-1]的地址为a + 4的地址p1[-1] == *(a + 4) == 5
继续分析p2

(int)a的值为0x0034fc50 ,(int)a + 1等于0x0034fc51
分析内存结构,测试环境字节序为little endian
01 00 00 00 02 00 00 00 03 00 0...
*p2等于0x2000000

16.可变参数列表

float average(int n, ...)
{
	va_list args;
	int i = 0;
	float sum = 0;
	va_start(args, n);
	for(i=0; i

17.宏函数实现的debug

#ifdef	DEBUG
#define debug(fmt,args...)	printf (fmt ,##args)
#define debugX(level,fmt,args...) if (DEBUG>=level) printf(fmt,##args);
#else
#define debug(fmt,args...)
#define debugX(level,fmt,args...)
#endif	/* DEBUG */

18.strcpy

char * strcpy( char *strDest, const char *strSrc ) 
{
 assert( (strDest != NULL) && (strSrc != NULL) );
 char *address = strDest;
 while( (*strDest++ = * strSrc++) != ‘\0’ );
 return address;
} 

19.strcat

char *  strcat (char * dst, const char * src)
{
	char * cp = dst;
	while( *cp )
	  cp++; /* find end of dst */
	while( *cp++ = *src++ ) ; /* Copy src to end of dst */
	return( dst ); /* return dst */
}	

20.strcmp

int strcmp(const char *s1, const char *s2)
{
  while (*s1 == *s2)
  {
	if (*s1 == 0)
	  return 0;
	s1++;
	s2++;
  }
  return *(unsigned const char *)s1-*(unsigned const char *)s2;
}

21.memcpy

void memcpy(void* pvTo, void* pvFrom, size_t size)
{
	void* pbTo = (byte*)pvTo;
	void* pbFrom = (byte*)pvFrom;
	ASSERT(pvTo != NULL && pvFrom != NULL); //检查输入指针的有效性
	ASSERT(pbTo>=pbFrom+size || pbFrom>=pbTo+size);//检查两个指针指向的内存是否重叠
	while(size-->0)
	*pbTo++ == *pbFrom++;
	return(pvTo);
}

22.实现字符串倒序显示

int invertedOrderStr()
{
   char* src = "hello,world";
   int len = strlen(src);
   char* dest = (char*)malloc(len+1);//要为\0分配一个空间
   char* d = dest;
   char* s = &src[len-1];//指向最后一个字符
   while( len-- != 0 )
   *d++=*s--;
   *d = 0;//尾部要加\0
   printf("%s\n",dest);
   free(dest);// 使用完,应当释放空间,以免造成内存汇泄露
   return 0;
}

23.二分查找算法

int BSearch(elemtype a[],keytype key,int n)
{
	int low,high,mid;
	low=0;high=n-1;
	while(low<=high)
	{
		mid=(low+high)/2;
		if(a[mid].key==key) return mid;
		else if(a[mid].key

24.冒泡排序

void BubbleSort(elemtype x[],int n)   //时间复杂度为0(n*n);
{
	int i,j;
	elemtype temp;
	for(i=1;ix[j+1].key)
			{
				temp=x[j];
				x[j]=x[j+1];
				x[j+1]=temp;
			}
		}
	}
}

25.位操作

#define BIT3 (0x1 << 3)
static int a;

void set_bit3(void) 
{
	a |= BIT3;
}
void clear_bit3(void) 
{
	a &= ~BIT3;
}

26.怎么判断链表中是否有环?

用两个指针来遍历这个单向链表,第一个指针p1,每次走一步;第二个指针p2,每次走两步;当p2 指针追上 p1的时候,就表明链表当中有环路了。
int testLinkRing(Link *head)
{  		
	Link *t1=head,*t2=head;
	while( t1->next && t2->next)
   	{
	     t1 = t1->next;
	     if (NULL == (t2 = t2->next->next))  
		return 0;//无环   
	     if (t1 == t2)  	
		return 1; 
	}
	return 0;
}

你可能感兴趣的:(总结)