C语言符号技巧总结

来源:互联网 发布:淘宝达人的粉丝怎么买 编辑:程序博客网 时间:2024/06/02 04:28
转载前注明出处,欢迎转载分享
接续符:

\可以作为接续符转义符,当作为接续符(\)使用时:
  • 编译器会将反斜杠剔除,跟在反斜杠后面的字符自动解到前一行
  • 在接续单词时,反斜杠之后不能有空格,反斜杠的下一行之前也不能有空格
  • 接续符适合在定义宏代码块时使用
来看如下代码:
 C Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include stdio.h >

#define SWAP(a, b)   \
{                              \
    
int temp a;         \
    b;                   \
    temp;             \
}

void swap(int a, int b)
{
    
int temp a;
    b;
    temp;
}

int main()
{
    
int 1;
    
int 2;

    SWAP(a, b);
    
//swap(a, b); 无法实现交换

    printf(
"a %d, %d\n"a, b);
    
return 0;
}

接续符的使用有利有弊,如果在代码中随意使用接续符,会使得代码变得非常混乱难读,但如果使用恰当(如上述代码),则会让代码变得非常小清新-。-

单引号和双引号:

来看下面的程序:
 C Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
#include stdio.h >
int main()
{
    
char *p1 1;
    
char *p2 '1';
    
char *p3 "1";

    printf(
"%s, %s, %s"p1, p2, p3);//段错误
    printf('\n');//段错误
    printf("\n");

    
return 0;
}

上述程序中:
p1 = 1; 意思是让p1指向内存地址为1的地方
p2 = '1'; 意思是让p2指向内存地址为49
p3 = "1"; 意思是让p3指向字符串“1”所对应的地址。

在操作系统里,低位的地址一般保留起来给操作系统用的。(从物理内存地址而言,操作系统一般都是放在低地址。从虚拟内存地址而言,操作系统一般都是放在高地址)这里所取的地址为物理地址。所以打印p1,p2的时候无法访问,即段错误(错误的访问了其他内存地址段)
1
printf(char *format, ...);
同样的对于:
1
printf('\n');
即就是对第一个参数进行赋值,代码如下: 
1
format '\n';

及指针指向了内存地址为10(\n的ASCII码值),为较低内存地址段,所以也会无法访问,产生段错误。

从编译器的角度,它认为p1,p2,p3的赋值是正确的,但是会有疑惑,因为用的人非常少,所以编译器会给出warning,但不会报错,所以p1,p2,p3的赋值是可以编译通过的,但随后也产生了段错误。

单引号和双引号的区别在于:
  • 'a'表示字符常量,在内存中占1个字节,'a'+1表示'a'的ASCII码加1,结果为'b'
  • “a”表示字符串常量,在内存中占2个字节,“a”+1表示指针运算,结果指向“a”结束符'\0'
看看如下错误代码: 
 C Code 
1
2
3
4
5
6
7
8
9
10
11
#include stdio.h >

int main()
{
    
char ";
    
while(c == "\t" || == " || == "\n")
    {
        scanf(
"%c"&c);
    }
    
return;
}

该代码想要表达的意思我们都懂,但是犯了很低级的错误。
char c = " ";
这行代码的意思是将存放字符串“ ”的首地址赋值给c,比如存放“”的首地址为32位的0xAABBCCDD,但是赋值给c时因为c仅有8位地址,所以会产生截断,及c =0xDD,也就是说定义的c为字符类型怎么能用双引号呢?该程序将所有双引号改为单引号后即正确。

对于单引号与双引号的总结:
  • 本质上单引号括起来的一个字符代表整数。
  • 双引号括起来的字符代表一个指针
  • C编译器接收字符和字符串的比较,可意义是错误的
  • C编译器允许字符串对字符变量赋值,其意义是可笑的
三目运算符(a?b:c):

三目运算符(a?b:c)可以作为逻辑运算符的载体
规则:当a的值为真时,返回b的值;否则返回c的值

看如下代码:
 C Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include stdio.h >

int main()
{
    
int 1;
    
int 2;
    
int 0;

    b;

    (a b) 
3;

    printf(
"%d\n"a);
    printf(
"%d\n"b);
    printf(
"%d\n"c);

    
return 0;
}

上述为错误代码,如果你认为结果输出的是:3,2,1那就错了。仔细分析一下这条语句: 

1
(a b) 3//等价于1 3;

三目运算符把a的值作为了左值,这明显是错误的,当然我们还是可以通过对其修改来达到想要的目的,而且还提升了编程B格,代码如下:
 
1
*(a &a &b) 3;

这条语句巧妙运用了指针的方法,其实也等同于下面的代码: 

1
2
(a &a &b);
*p 3;

位运算的移位运算符(<<,>>)小技巧:
  • 右移n位相当于乘以2的n次方,但效率比数学运算符高。
  • 右移n位相当于除以2的n次方,但效率比数学运算符高。
交换运算: 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define SWAP1(a, b)    \
{                                \
    
int temp a;           \
    b;                     \
    temp;               \
}                                \

#define SWAP2(a, b)    \
{                                \
    b;                \
    b;                \
    b;                \
}                                \

#define SWAP3(a, b)    \
{                                \
    b;                \
    b;                \
    b;                \
}                                \
这样的写法就是对上面所写的接续符的运用,比较这三种交换运算,有如下特点:

第一种交换相比另外两种交换,多定义了一个临时变量进而实现交换

第二种交换方式不细说了,但是第二种交换有一定的优点和缺点,优点在于其交换只用到了两个变量,不需要再申请一个临时存放变量,但缺点在于当执行a= a +b;时,如果a和b的值非常大,两个变量相加则可能产生越界,所以第二种交换在特定条件下不会得到我们预期的结果。

第三种交换是通过异或实现交换,先将a = a ^b;第二条语句即b = a ^ b ^ b;就是让b = a; 第三条语句是a = a ^ b ^ a;即a = b;进而产生交换,这种按位运算在C中运算效率非常高,我们之前一直使用第一种交换,是因为第一种交换相比第三种也有其优势,第三种交换不足在于它仅适用于整型数,但不会产生像第二种交换那样所产生的越界问题,第三种交换方法是我们真正学习C应该掌握的内容。

面试题详解:
有一个数列2,3,5,7,2,2,2,5,3,7,1,1,1,其中的自然数都是以偶数的形式出现,只有一个自然数出现次数为奇数次,编写程序找出这个自然数。

思路1:给数列排序,遍历并且计数,判断计数是否能被2整除即可。(缺点:排序耗时)

思路2:申请数组a[],数组b[],遍历利用b[a[i]]++判断b数组奇偶性来确认出现的次数,类似桶排序原理。(换来时间,耗费空间)

思路3(推荐):将数列每个数取异或位运算,及2^3^5^7^2^2^2^5^3^7^1^1^1,最终为奇数的数即可找出。(省时间,省空间)
代码如下:
 C Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include stdio.h >

#define DIM(a) (sizeof(a)/sizeof(*a))

int main()
{
    
int a[] {2357222537111};
    
int find 0;
    
int 0;

    
for(i 0DIM(a); i++)
    {
        find find a[i];
    }

    printf(
"find %d\n"find);

    
return 0
;
}

++,--操作符
 C Code 
1
2
3
4
5
6
7
8
9
10
11
#include stdio.h >

int main()
{
    
int 2;
    
int (++i) (++i) (++i);

    printf(
"%d\n"j);

    
return 0;
}

看上述代码后你能猜出输出结果是多少吗?
 
1
int (++i) (++i) (++i);

对于编译器来说,不同的编译器给出不同的值,有些编译器认为先将这三个i相加,然后执行三次++操作,结果为9另一些编译器(gcc和g++下)认为先结合前两项即i+i,然后执行两次++操作,再与第三个i结合,执行一次++操作,结果为13。

 C Code 
1
2
3
4
5
6
7
8
9
10
11
#include stdio.h >

int main()
{
    
int 2;
    
int ++i++ +i;

    printf(
"%d\n"j);

    
return 0;
}

这个代码的结果又是多少呢?

这是一个错误代码,错误在于:
 
1
int ++i+++i;

首先对于编译器来说,它会从左向右的顺序一个一个尽可能多的读入字符,当即将读入的字符不可能和已读入的字符组成合法符号为止,所以它认为读完++i后会再读++,发现读完++后读不下去了才会停止,也就是说上述语句等价于:
 
1
int (++i++) i;

这也就等价于:

1
int (3++) i;

上述语句3++明显是错误的表示,所以编译如上代码时会出错。

示例代码:
 C Code 
1
2
3
4
5
6
7
8
9
10
11
#include stdio.h >

int main()
{
    
int 2;
    
int i++ +i++ +i;

    printf(
"i %d, %d\n"i, j);

    
return 0;
}
结果为i = 4, j =6。因为i+++i+++i相当于先执行三次相加操作,即2+2+2,这是j的值。执行完语句后则会执行两次++操作,所以i由2变为4。

优先级

易错优先级表:
C语言符号技巧总结

看如下代码:
 C Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
#include stdio.h >

int main()
{
    
char 'c';
    
short 0;

    c;

    printf(
"%d\n"sizeof(s c));

    
return 0;
}
大家知道short的优先级高于char,所以认为输出结果为2,实际上输出结果为4。当char与short相加时会产生隐式转换,即产生为int类型。

参考隐式转换表如下:

C语言符号技巧总结
0 0
原创粉丝点击