走进C的世界-那些年我们常犯的错---keyword相关
近期一段时间參加一些面试,发现非常多细节的问题自己已经变得非常模糊了。对一些曾经常常遇到的错误。如今也说不出原因了。
而且在编码过程中也相同犯这些错误。
特别写一个博客来记录这些我们常常遇到的错误。自己也在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;
}
*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 > 6
a + b > 6
TOTAL_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 是keyword而不是函数,看以下的代码:
/*File : sizeof.c
*Auth : sjin
*Date : 20141018
*Mail : 413977243@qq.com
*/
#include <stdio.h>
/*sizeof 是keyword,不是函数
*计算数据空间的字节数
* */
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);
}
*Auth : sjin
*Date : 20141018
*Mail : 413977243@qq.com
*/
#include <stdio.h>
/*sizeof 是keyword,不是函数
*计算数据空间的字节数
* */ 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
##[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 前面加一个keyword?类型扩展?明显不对。我们能够在 int 前加 unsigned,const 等keyword但不能加 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) = 8
union 联合体
考虑存储模式:大端模式和小端模式。
大端模式(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;
}
*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的世界-那些年我们常犯的错---keyword相关的更多相关文章
- 惊呆了!Java程序员最常犯的错竟然是这10个
和绝大多数的程序员一样,我也非常的宅.周末最奢侈的享受就是逛一逛技术型网站,比如说 programcreek,这个小网站上有一些非常有意思的主题.比如说:Java 程序员最常犯的错竟然是这 10 个, ...
- MySQL数据库设计常犯的错以及对性能的影响
1.过分的反范式化为表建立太多的列 我们在设计数据库的结构时,比较容易犯的第一个错误就是对表进行了过分的反范式化的设计,这就容易造成了表中的列过多,虽然说Mysql允许为一个表建立很多的列,但是由于M ...
- C#7.2——编写安全高效的C#代码 c# 中模拟一个模式匹配及匹配值抽取 走进 LINQ 的世界 移除Excel工作表密码保护小工具含C#源代码 腾讯QQ会员中心g_tk32算法【C#版】
C#7.2——编写安全高效的C#代码 2018-11-07 18:59 by 沉睡的木木夕, 123 阅读, 0 评论, 收藏, 编辑 原文地址:https://docs.microsoft.com/ ...
- [C#] 走进 LINQ 的世界
走进 LINQ 的世界 序 在此之前曾发表过三篇关于 LINQ 的随笔: 进阶:<LINQ 标准查询操作概述>(强烈推荐) 技巧:<Linq To Objects - 如何操作字符串 ...
- 走进缓存的世界(三) - Memcache
系列文章 走进缓存的世界(一) - 开篇 走进缓存的世界(二) - 缓存设计 走进缓存的世界(三) - Memcache 简介 Memcache是一个高性能的分布式内存对象缓存系统,用于动态Web应用 ...
- 小丁带你走进git的世界三-撤销修改
一.撤销指令 git checkout还原工作区的功能 git reset 还原暂存区的功能 git clean 还没有被添加进暂存区的文件也就是git还没有跟踪的文件可以使用这个命令清除他们 g ...
- 小丁带你走进git的世界二-工作区暂存区分支
小丁带你走进git的世界二-工作区暂存区分支 一.Git基本工作流程 1.初始化一个仓库 git init git clone git仓库分为两种情况: 第一种是在现有项目或目录下导入所有文件到 ...
- C#新手常犯的错误
虽然这篇post的标题是新手常犯的错误,实际上很多有经验的程序员也经常犯这些错误,我整理了一下,就当是笔记.1.遍历List的错误,比如如下代码: List<String> strList ...
- Java开发人员最常犯的10个错误
这个列表总结了10个Java开发人员最常犯的错误. Array转ArrayList 当需要把Array转成ArrayList的时候,开发人员经常这样做: List<String> list ...
随机推荐
- BPL vs. DLL
第一部分:有关包的介绍 一般我们编写编译一个DELPHI应用程序时,会产生一个EXE文件,也就是一个独立的WINDOWS应用程序.很重要的一点:区别于Visual Basic,DELPHI产生的是预先 ...
- ThinkPHP3.2 常量参考
原文:ThinkPHP3.2 常量参考 预定义常量 预定义常量是指系统内置定义好的常量,不会随着环境的变化而变化,包括: URL_COMMON 普通模式 URL (0) URL_PATHINFO PA ...
- [C++]函数参数浅析
Date:2014-1-9 Summary: 函数参数相关记录 Contents:1.形参实参 形参:用于接收值的变量被称为形参 实参:传递给函数的值被称为实参 2.函数的参数传递之后2种 a).值传 ...
- loj1245(数学)
传送门:Harmonic Number (II) 题意:求sum=n/1+n/2+n/3+...+n/n.(n<2^31) 分析:在一定的区间内n/i的值是一定的,因此要跳过这段区间来加速求解. ...
- Shell脚本检查memcache进程并自己主动重新启动
修正版: #!/bin/sh #check memcache process and restart if down mm_bin="/usr/local/bin/memcached&quo ...
- Linux下安装Python3.3.0
Linux下安装Python3.3.0_路易_新浪博客 Linux下安装Python3.3.0 (2013-01-08 11:45:37)
- hdu 5015 233 Matrix(构造矩阵)
http://acm.hdu.edu.cn/showproblem.php?pid=5015 由于是个二维的递推式,当时没有想到能够这样构造矩阵.从列上看,当前这一列都是由前一列递推得到.依据这一点来 ...
- Jquery插件placeholder的用法
闲的蛋疼,演示一下Jquery插件placeholder的用法,借助该插件能够轻松实现HTML5中placeholder特效: 效果图: 实现代码: <%@ page language=&quo ...
- java.util.concurrent.ThreadPoolExecutor
java.util.concurrent.ThreadPoolExecutor An ExecutorService that executes each submitted task using o ...
- LeetCode: Valid Palindrome [125]
[题目] Given a string, determine if it is a palindrome, considering only alphanumeric characters and i ...