走进C的世界-那些年我们常犯的错---关键字相关
2014-10-18 11:04
253 查看
最近一段时间参加一些面试,发现很多细节的问题自己已经变得很模糊了。对一些以前经常遇到的错误,现在也说不出原因了。并且在编码过程中也同样犯这些错误。特别写一个博客来记录这些我们经常遇到的错误。自己也在gitHUb上创建了一个库,来总结这些错误。地址:https://github.com/jinshaohui/C_Error_problem,希望大家有遇到相同问题的都提交到这里。
废话少说,来正题吧,先来说说数据类型使用过程中遇到的问题吧!
看下面的代码会输入什么?为什么 ?
/*File : type_conversion.c *Auth : sjin *Date : 20141018 *Mail : 413977243@qq.com */ #include <stdio.h> int array[] = {10,20,30,40}; #define TOTAL_EMEMENTS (sizeof(array)/sizeof(array[0])) /*另外一个例子 *代码输出什么?为什么? * */ int fun() { unsigned int a = 6; int b = -20; (a + b > 6)? puts("a + b > 6"):puts("a + b < 6"); } int main() { int d = -1; if (d <= TOTAL_EMEMENTS){ printf("##[sjin] : the array total emements is %d\n",TOTAL_EMEMENTS); }else { printf("##[sjin] : the array is empty!\n"); } fun(); return 0; }
输入如下:
##[sjin] : the array is empty! a + b > 6TOTAL_EMEMENTS 所定义的值是unsigned int类型(因为sizeif返回值是无符号的),if语句在unsigned int 和int之间测试相等性 ,所以d被升级为unsigend int类型,-1转换为unsigned int类型是一个巨大的整数,致使表达式的值为假。所以在比较前,需要对宏定义前加int类型的强制转换。这个问题就是潜在类型转换导致的。函数fun()中的错误也同样是这个问题。
在对宏定义中,使用下面语句:
#define TOTAL_EMEMENTS (sizeof(array)/sizeof(array[0]))而不是:#define TOTAL_EMEMENTS (sizeof(array)/sizeof(int))
很明显,在代码可扩展性方面,我们在不改变#define语句的情况下可以随意改变数据的基本类型(int变成char).
sizeof 是关键字而不是函数,看下面的代码:
/*File : sizeof.c *Auth : sjin *Date : 20141018 *Mail : 413977243@qq.com */ #include <stdio.h> /*sizeof 是关键字,不是函数 *计算数据空间的字节数 * */ void fun(char b[100]) { printf("##[sjin] in fun sizeof(b):\t%d \n",sizeof(b)); } int main() { double *p = NULL; char a[100] = {'\0'}; double *(*b)[3][6]; int d = 0; printf("##[sjin] sizeof(int):\t%d \n",sizeof(int)); printf("##[sjin] sizeof(d):\t%d \n",sizeof(d)); printf("##[sjin] sizeof d:\t%d \n\n",sizeof d ); //下面编译错误 //printf("##[sjin] sizeof int:\t%d \n\n",sizeof int ); printf("##[sjin] sizeof(p):\t%d \n",sizeof(p)); //p是一个指针 printf("##[sjin] sizeof(*p):\t%d \n",sizeof(*p));//*p 为一个double的变量 printf("##[sjin] sizeof(a):\t%d \n",sizeof(a));//a是一个数组 printf("##[sjin] sizeof(a[0]):\t%d \n",sizeof(a[0]));//a[0] 是一个char型变量 printf("##[sjin] sizeof(&a):\t%d \n",sizeof(&a));//&a 是a的地址 printf("##[sjin] sizeof(&a[0]):\t%d \n",sizeof(&a[0]));//&a[0] 是a[0]的地址 printf("##[sjin] sizeof(b):\t%d \n",sizeof(b)); printf("##[sjin] sizeof(*b):\t%d \n",sizeof(*b)); printf("##[sjin] sizeof(**b):\t%d \n",sizeof(**b)); printf("##[sjin] sizeof(***b):\t%d \n",sizeof(***b)); printf("##[sjin] sizeof(****b):\t%d \n",sizeof(****b)); fun(a); }输入如下:
##[sjin] sizeof(int): 4 ##[sjin] sizeof(d): 4 ##[sjin] sizeof d: 4 ##[sjin] sizeof(p): 4 ##[sjin] sizeof(*p): 8 ##[sjin] sizeof(a): 100 ##[sjin] sizeof(a[0]): 1 ##[sjin] sizeof(&a): 4 ##[sjin] sizeof(&a[0]): 4 ##[sjin] sizeof(b): 4 ##[sjin] sizeof(*b): 72 ##[sjin] sizeof(**b): 24 ##[sjin] sizeof(***b): 4 ##[sjin] sizeof(****b): 8 ##[sjin] in fun sizeof(b): 4
sizeof int 表示什么啊?int 前面加一个关键字?类型扩展?明显不正确,我们可以在 int 前加 unsigned,const 等关键字但不能加 sizeof。好,记住:sizeof 在计算 变量所占空间大小时, 括号可以省略.
来说下函数传参,数组作为形参时,当作是一个指针, 所以是一个指针的大小。
double *(*b)[3][6]; 这个比较难理解。b是一个指向double *[3][6] 类型的指针。所以大小也为4个字节。
*b 是表示一个double*【3】【6】多维数组的类型,而数组元素都是double*类型的指针,所以
sizeof(*b) = 3*6*sizeof(double*) = 72
**b是表示一个 double*【6】的数组,所以sizeof(**b) = 6*sizeof(double*) = 24
***b是表示一个double*的元素指针,所以sizeof(***b) = sizeof(double*) = 4
****b是表示一个double类型的数值,所以sizeof(****b) = sizeof(double) = 8union 联合体 考虑存储模式:大端模式和小端模式。
大端模式(Big_endian) :字数据的 高字节存储在 低地址中,而字数据的 低字节则存放
在 高地址中。
小端模式(Little_endian) :字数据的 高字节存储在 高地址中,而字数据的 低字节则存放
在 低地址中。
union 型数据所占的空间等于其最大的成员所占的空间。对 union 型的成员的存取都是
相对于该联合体基地址的偏移量为 0 处开始, 也就是联合体的访问不论对哪个变量的存取都
是从 union 的首地址位置开始。
/*File : union.c
*Auth : sjin
*Date : 20141022
*Mail : 413977243@qq.com
*/
#include <stdio.h>
/*这里来说明面试中经常问的一个问题
*判断系统的大小端问题
*存储模式:大端模式和小端模式。
* 大端模式(Big_endian) :字数据的 高字节存储在 低地址中,而字数据的 低字节 则存放在高地址中。
* 小端模式(Little_endian) :字数据的 高字节存储在 高地址中,而字数据的 低字节则存放在低地址中。
*union 型数据所占的空间等于其最大的成员所占的空间。对 union 型的成员的存取都是
*相对于该联合体基地址的偏移量为 0 处开始, 也就是联合体的访问不论对哪个变量的存取都
*是从 union 的首地址位置开始.
*/
/* True: 小端模式
* False:大端模式
*/
int checkSystem()
{
union check{
int i;
char ch;
}c;
c.i = 1;
return (c.ch == 1);
}
int main()
{
if(checkSystem()){
printf("当前系统为小端模式\n");
}else{
printf("当前系统为大端模式\n");
}
return 0;
}
声明器是C语言声明的非常重要成份,他是所有声明的核心内容,简单的说:声明器就是标识符以及与它组合在一起的任何指针、函数括号、数组下表等
/*File : union.c *Auth : sjin *Date : 20141022 *Mail : 413977243@qq.com */ /*介绍一些声明,及const、typedef的用法 */ #include <stdio.h> typedef void(*ptr_to_func)(int); /*它表示ptr_to_func是一个函数指针,该函数 *接受一个int参数,返回值为void */ ptr_to_func signal(int,ptr_to_func); /*它表示signal是一个函数,它接收两个参数 * 其中一个是int型,另一个是ptr_to_func, *返回值是ptr_to_func型 */ int main() { char (*j)[20];//j是一个指向数组的指针,数组内有20个char型的元素 j = (char (*)[20])malloc(20*sizeof(char));//申请空间 int const a;//a是一个常整型 int const *a;//a是一个指向常整型的指针 const int *a;//a是一个指向常整型的指针 int *const a;//a是一个指向整型的常指针 const int *const a;//a是一个指向常整型的常指针 char *const *(*next)(); /*next 是一个指针,它指向一个函数,该函数的返回值是另一个指针 * 该 指针指向一个类型为char的常指针 */ return 0; }
具体声明的解析方法,可参见这个博文
相关文章推荐
- 走进C的世界-那些年我们常犯的错---strcpy及memcpy函数
- 走进C的世界-那些年我们常犯的错---keyword相关
- 带领我们一起走进PHP的世界
- Ext.Net控件,简单案例1,让我们从Hello World开始,走进Ext.Net控件的世界!。
- 那些年我们追过的C#奇葩关键字——忐忑[转载]
- 走进C++程序世界-----函数相关(全局变量,默认参数,函数重载,内联函数)
- 走进windows编程的世界-----绘图相关
- 走进C++程序世界-----函数相关(全局变量)
- 走进windows编程的世界-----画图相关
- 【有奖征文】走进VR开发世界(1)——我们离开发一款VR大作还有多远?
- 那些年我们追过的C#奇葩关键字——忐忑
- 【有奖征文】走进VR开发世界——我们离开发一款VR大作还有多远?
- 那些年我们踩到过的坑(二):3.1 版 MultiThreadedHttpConnectionManager 未releaseConnection导致应用服务器宕机
- 那些年,我们一起学WCF--(7)PerSession实例行为
- 让我们一起来鸟瞰世界高薪CEO们
- 那些年,我们用过的地图下载器
- 我是一只小小鸟————走进编程世界。
- 网络编程<二>---那些年我们一起学习linux程序设计
- 那些年,我们被耍过的bug——haslayout
- 那些年,我们一起追过的女孩-会声会…