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
C Code 1
2
3
4
5
6
7
8
9
10
11
12
13
1
1
1
C Code 1
2
3
4
5
6
7
8
9
10
11
C Code 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
1
1
2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
C Code 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
C Code 1
2
3
4
5
6
7
8
9
10
11
1
C Code 1
2
3
4
5
6
7
8
9
10
11
1
1
1
C Code 1
2
3
4
5
6
7
8
9
10
11
C Code 1
2
3
4
5
6
7
8
9
10
11
12
13
接续符:
\可以作为接续符和转义符,当作为接续符(\)使用时:
- 编译器会将反斜杠剔除,跟在反斜杠后面的字符自动解到前一行
- 在接续单词时,反斜杠之后不能有空格,反斜杠的下一行之前也不能有空格
- 接续符适合在定义宏代码块时使用
来看如下代码:
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; \
a = b; \
b = temp; \
}
void swap( int a, int b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int a = 1;
int b = 2;
SWAP(a, b);
//swap(a, b); 无法实现交换
printf( "a = %d, b = %d\n" , a, b);
return 0;
}
{
}
{
}
{
}
接续符的使用有利有弊,如果在代码中随意使用接续符,会使得代码变得非常混乱难读,但如果使用恰当(如上述代码),则会让代码变得非常小清新-。-
单引号和双引号:
来看下面的程序:
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的时候无法访问,即段错误(错误的访问了其他内存地址段)。
printf(char *format, ...);
同样的对于:
printf('\n');
即就是对第一个参数进行赋值,代码如下:
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'
看看如下错误代码:
2
3
4
5
6
7
8
9
10
11
#include < stdio.h >
int main()
{
char c = " " ;
while(c == "\t" || c == " " || c == "\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的值
看如下代码:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include < stdio.h >
int main()
{
int a = 1;
int b = 2;
int c = 0;
c = a < b ? a : b;
(a < b ? a : b) = 3;
printf( "%d\n", a);
printf( "%d\n", b);
printf( "%d\n", c);
return 0;
}
{
}
上述为错误代码,如果你认为结果输出的是:3,2,1那就错了。仔细分析一下这条语句:
(a < b ? a : b) = 3; //等价于1 = 3;
三目运算符把a的值作为了左值,这明显是错误的,当然我们还是可以通过对其修改来达到想要的目的,而且还提升了编程B格,代码如下:
*(a < b ? &a : &b) = 3;
这条语句巧妙运用了指针的方法,其实也等同于下面的代码:
2
p = (a < b ? &a : &b);
*p= 3;
*p
位运算的移位运算符(<<,>>)小技巧:
- 右移n位相当于乘以2的n次方,但效率比数学运算符高。
- 右移n位相当于除以2的n次方,但效率比数学运算符高。
交换运算:
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; \
a = b; \
b = temp; \
} \
#define SWAP2(a, b) \
{ \
a = a + b; \
b = a + b; \
a = a + b; \
} \
#define SWAP3(a, b) \
{ \
a = a ^ b; \
b = a ^ b; \
a = a ^ 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,最终为奇数的数即可找出。(省时间,省空间)
代码如下:
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))
intmain()
{
int a[] = { 2, 3, 5, 7, 2, 2, 2, 5, 3, 7, 1, 1, 1};
int find = 0;
int i = 0;
for(i = 0; i < DIM(a); i++)
{
find = find ^ a[i];
}
printf( "find = %d\n" , find);
return 0;
}
int
{
}
++,--操作符
2
3
4
5
6
7
8
9
10
11
#include < stdio.h >
int main()
{
int i = 2;
int j = (++i) + (++i) + (++i);
printf( "%d\n", j);
return 0;
}
{
}
看上述代码后你能猜出输出结果是多少吗?
int j = (++i) + (++i) + (++i);
对于编译器来说,不同的编译器给出不同的值,有些编译器认为先将这三个i相加,然后执行三次++操作,结果为9;另一些编译器(gcc和g++下)认为先结合前两项即i+i,然后执行两次++操作,再与第三个i结合,执行一次++操作,结果为13。
2
3
4
5
6
7
8
9
10
11
#include < stdio.h >
int main()
{
int i = 2;
int j = ++i++ +i;
printf( "%d\n", j);
return 0;
}
{
}
这个代码的结果又是多少呢?
这是一个错误代码,错误在于:
int j = ++i+++i;
首先对于编译器来说,它会从左向右的顺序一个一个尽可能多的读入字符,当即将读入的字符不可能和已读入的字符组成合法符号为止,所以它认为读完++i后会再读++,发现读完++后读不下去了才会停止,也就是说上述语句等价于:
int j = (++i++) + i;
这也就等价于:
int j = ( 3++) + i;
上述语句3++明显是错误的表示,所以编译如上代码时会出错。
示例代码:
2
3
4
5
6
7
8
9
10
11
#include < stdio.h >
int main()
{
int i = 2;
int j = i++ +i++ +i;
printf( "i = %d, j = %d\n" , i, j);
return 0;
}
{
}
结果为i = 4, j =6。因为i+++i+++i相当于先执行三次相加操作,即2+2+2,这是j的值。执行完语句后则会执行两次++操作,所以i由2变为4。
优先级
易错优先级表:
看如下代码:
2
3
4
5
6
7
8
9
10
11
12
13
#include < stdio.h >
int main()
{
char c = 'c';
short s = 0;
s = c;
printf( "%d\n", sizeof(s + c));
return 0;
}
{
}
大家知道short的优先级高于char,所以认为输出结果为2,实际上输出结果为4。当char与short相加时会产生隐式转换,即产生为int类型。
参考隐式转换表如下:
0 0
- C语言符号技巧总结
- C语言中的符号的技巧
- c语言之符号的技巧
- C语言无符号有符号比较问题总结
- C语言学习记录(二):符号的技巧
- c语言中的重定向符号<与>总结
- C语言基础总结-符号的输入输出(常用)
- C、C++等语言常见符号作用总结
- [总结] C语言的位操作技巧
- C语言符号优先级
- C语言符号优先级
- c语言符号"->"
- c语言运算符号
- C语言中的符号
- c语言运算符号
- c语言运算符号
- C语言中的符号
- C语言中的符号
- const和volatile详解
- 引入jar包之Error:Execution failed for task ':app:transformClassesWithDexForDebug
- JUST SO SO之mybatis 缓存机制
- 结构体及柔性数组
- Centos安装gcc及g++
- C语言符号技巧总结
- Linux-Centos下的git配置使用
- gcc与g++的区别(转贴)
- 关于Git的安装
- linux内核进程创建分析
- 编译预处理
- 动态申请内存及相关补充
- Mongodb解析
- C/S架构的简单文件传输系统的实现