您的位置:首页 > 编程语言 > C语言/C++

C语言——读书心得1

2015-01-25 15:55 176 查看
1:/  两个实数相除为双精度, 5/3存在不同系统效果不同,大多数是向零靠齐
  %  符号两边的参数类型要相同
  其他符号的操作数都可以是任何类型
  a=(int)x
 如果x为float型变量,a为整形变量,进行强制类型运算后得到一个为int型的临时变量,
 a的值为x的整数部分,注意x的类型与值都没改变。赋值结束后就不再存在
2.预处理:
删除注释
替换宏定义
插入由#include包含的内容
确定代码的部分内容是否应该根据一些条件编译指令进行编译

3.字符串常量会生成一个指向字符串常量指针,其直接值就是一个地址

声明指针技巧 int  *a,*b,*c; 比  int*  a, int*  b; 好

4.#define d char*   只是替换     d a,b;中b为char型,a为char
typedef d char*   类型重命名   d a,b;中b和a为char *

5.函数的两种不同写法:(风格不同)
int
main()
int main

6.printf()函数返回它所要打印的字符数
7.C语言中的 if() 因为无布尔类型只能用整型的 0 或 非0 来表示真假 ()里的表达式只会返回0和非
8.for(expression1,expression2,expression3)
  statement
expression1只执行一次即是初始化部分,expression2 为条件部分 每次循环体都要执行一次 expression3 为调整部分
for语句中 break 表示立即退出循环  continue 表示把控制流直接转移到调整部分,不去本次循环的条件部分
                             while中的continue把本次循环的条件部分和调整部分都跳过去了
例如:
#include<stdio.h>
int main()
{

 int i,j;
for(i=0;i<10;i++)
{
 if(i==4)
       continue;
  printf("%d ",i);
}
printf("\n");
j=0;
while (j<10)
{  
  if(j==4
   continue;
  printf("%d ",j);
j++;

}
return 0;
}

9.switch()括号内的表达式结果必须是  整形
  case 只是给了一个入口   break可以解决划分的问题
  最好在最后一个case语句之后加上break
  default解决任何非法值
10.移位操作符   左移右边补零
                         右移
                 左边补零为逻辑移位
                 左边补符号位为算术移位   (   编译器而定)vc6.0  linux为算数移位  
               移位的位数比操作数位数还多 右移 vc6.0 linux 得数全为零
11.复合赋值赋效率比较高
   a[x+y*2]=a[x+y*2]+1;
    a[x+y*2]+=1;
前者 计算两次下标表达式 后者 只计算一次下标表达式
12.++ --  这些只是作用于所修改变量的拷贝
     ++a=10;不合法,因为不能向一个值进行赋值
13.关系操作符返回的是一个整型 0 或 1,因为C语言无bool类型,所以他们可以赋值给整型变量,而且还可以用于if  while语句中 当作 测试表达式
14. 系统不提供数组下标的合法判断 
15.case 后边整型常量表达式
16.左值是位置,右值是数
17.    int   a,b;

         long   c   =(long)a*b;
18.#include<stdio.h>
int main()
{
 int a[5]={1,2,3,4,5};
 int *ptr=(int *)(&a+1);
 //*(a+1)   (&a+1)
return 0;
}
//&(a+1) 为下一个数组的首地址  &a+5*sizeof(int)
19.指针变量的名字是编译器与地址进行关联的,硬件任然只能通过地址访问内存位置
20.变量里边的是一系列0或者1  如果使用的是整形指令就解释为整型,如果使用的是浮点型指令那就解释为浮点数,可以告诉我们不能通过检查一个指的位来判断它类型
所以  变量 类型定义有两个作用  一是 为其分配内存   二是 用哪种指令来解析这种二进制数
21.C语言大小写敏感即是大小写表示不同的变量,WINDOWS的大小写是一个值

22.只有当两个指针都指向同一个数组中的元素的时候,才允许一个指针减去另外一个指针
23.for(p=&values[N-1];vp>=&value[0];vp--)

    {
     *vp=0;
     }
最后结束时VP指向&value[0]后边的那一个地址,暂且称为&value[-1]
标准允许&value[N-1]到&value[1]与&value[0]比较
   不允许&value[-1]与&value[0]比较

24.递归
void  binary_to_ascii(unsigned int value)
{
   unsigned int q;
   q=value/10;
   if(q!=0)
    binary_to_ascii(q);
       putchar(value%10+'0');

}
25,int x=5;
        if(6<x<10)
            printf(" 1");
              else
                   printf(" 0");
             6<x  为假    就是0
             0<10 为真  即是 1
         所以就输出 1;
26.#define PRINT(x)    printf("%d",x);
    int main()
   {
    int   a=10;
   if(a>1)
 {                                          //if的这两个括号一定要加上,因为
宏替换
     PRINT(a) ;                      //  printf("%d",a);;
     多了一个分号即是一个空语句     如果没有括号   else  就找不到 对应的 if  
}  
     else
{
        printf("hahahaha");
}
return ;
              
    }
27.c=3.56f   含义是  3.56 用浮点数  存储
28.for(表达式1,表达式2,表达式3)
{
语句块
}
表达式 1:初始化只执行一次
表达式2:每次循环都执行,多次执行
表达式3:语句块执行完之后多次执行
28.如果所申请的内存块大于目前堆上剩余内存块(整块),则内存分配
会失败,函数返回NULL。注意这里说的“堆上剩余内存块”不是所有剩余内存块之和,因
为malloc函数申请的是连续的一块内存。
既然malloc函数申请内存有不成功的可能,那我们在使用指向这块内存的指针时,必
须用if(NULL!=p)语句来验证内存确实分配成功了。
29.释放完块内存之后,没有把指针置NULL,这个指针就成为了“野指针”,也有书叫“悬
垂指针”。这是很危险的,而且也是经常出错的地方。所以一定要记住一条:free完之后,
一定要给指针置NULL。
30.    
int my_strlen( const char* strDest )
{
assert(NULL != strDest);
return ('\0' != *strDest)?(1+my_strlen(strDest+1)):0;
递归的效率很低,递归的深度太大甚
至可能出现错误(比如栈溢出)。所以,平时写代码,不到万不得已,尽量不要用递归

31.在多维数组中只有一维数组可以省略,其余几个维数一定要写出来
32.    fun(int *v)
          fun(int v[])//两种都可以
int vector[10]
     fun(vector)

fun(int (*v)[10])//数组指针
fun(int v[][10])//10必须有
int  array[3][10]
fun(array)

33.     数组指针的应用
#include<stdio.h>
#include<string.h>
char const keyword[]={"do","for","if","while"};
#define N  (sizeof(keyword)/sizeof(keyword[0]))
int
lookup_keyword(char const *const desired_word,char const *keyword_table[],int const size)
{
char const **k;
for(k=keyword_table;k<keyword_table+size;k++)
if (0==strcmp( desired_word,*k))
    return k-keyword_table;
    else
  //没有找到
  return -1;

}

int main()
{
 
   // char const keword[][6]={  "do","for","if","while" };
 //只对形参和局部变量声明进行修改就行
 //char const *keyword[]={  "do","for","if","while" ,NULL };
 //for(k=keyword_table;*k!=NULL;k++)

lookup_keyword("do",keyword_table[],N)
return 0;
}
34.int a[2][3]
a 为数组指针,常量指针
a+1  =*(a+1)=a[1]为数组第二行的首地址
       访问第二行第一个元素  *(*(a+1))=a[1][0]=*(*(a+1)+0)
*(a+1)+ 1 也是 一个地址  
*(*(a+1)+1)  是值      等价于  a[1][1]
35.静态变量在程序载入内核时就得到了初始值
   自动变量(包括数组)每次当执行流进入它们申明所在的代码块时都要用隐士的赋值语句进行赋值

36.编译器为一个结构体变量成员分配内存时要满足他们的边界对齐的要求。在实现结构存储的边界对齐时,可能会浪费一些内存空间。边界对齐可以最大限度的减少浪费内存空间。  sizeof 返回的的值也包括结构体里面浪费的内存空间。

结构体可以作为参数传递给函数,也可以作为返回值从函数返回。但是向结构体传递一个结构体指针的效率更高。在结构体参数生命时可以加上const
关键字防止函数修改指针所指的结构体的内容。
37.程序编译过程
                              1.——预处理(宏替换,注释用空格替换)
               ——1.编译——2.解析(找错误)——产生目标代码————————3
翻译环境————————————————————1.库函数——————3  
               ————————————————————————2.链接器——3   产生可执行文件
                (obj程序因为需要连接库函数的实体才可以执行,但是在生成它的时候吴库函数实体,所以不可以执行)
              ————1.载入内核(静态变量初始化)类似  ARM板需要手动载入
执行过程———2.执行程序  (小启动程序(调用main函数)+程序(运行时栈的 临时变量初始化))
                          3.程序终止 (正常和异常)
38.动态内存分配
    静态分配内存在编译时已经分配好了
    运行时可以动态分配内存(堆中)
 malloc 返回指向被分配内存块的起始地址(是一块连续的地址,不会分为多块),实际编译器分配的内存比你要求的稍微多一点,但是这一点是编译器决定的有时候不够用了,你也不能奢望编译器多分配一点,若malloc无法向操作系统要取得内存就返回一个NULL指针,malloc返回的是一个void*
  realloc用于修改原先分配好的内存,增加或者减少  原先的内存内容保留   如果原先的内存无法改变大小,就将分配一块新的内存 并将原来的复制过来     所以在使用realloc 后要改用realloc的新指针,原来的指针无法再用
  如果 realloc的第一个参数为NULL就与malloc的作用一样了
39.宏定义时  的接续符 \  后边不能接任何代码  宏名 要大写  一种规定
     valatile 关键字 是告诉编译器不要进行代码的优化  
例如一个全局变量  int a=10;
         当不加上  valatile  关键字时  编译器会将这个全局变量放到寄存器中以实现优化速度,不再从内存中去这个值。
        当我们用类似引用来改变这个值是就只是更新了内存中的值,但是编译器输出时在寄存器中读取这个全局变量,就会出现错误
         因此可以加上这个关键字不让编译器优化代码
  在C语言中  int const a=10   这里的a是一个人变量  不能作为数组的长度 (这个变量只是不允许改变罢了)   但是在C++中就是一个常量
40.register  所修饰的变量必须是一个单一的长度小于或等于整型的值,而且register 变量可能不存储在内存中 所有不可与用  &  来访问这个变量的地址
41.
#include<stdio.h>

int main()

{

int a[5]={1,2,3,4,5};
int *ptr1=(int *)(&a+1);
int *ptr2=(int *)((int)a+1);
printf("%x\n%x\n",ptr1[-1],*ptr2);

return 0;

}
  输出结果是 5  2000000

 ptr1[-1]=*(ptr1-1)  
&a 代表整个数组  所以ptr指向 整个数组后边的一个整型大小的内存,*(ptr1-1)就又指向了数组最后一个元素5

a[5]数组在 内存中是这样存储(小端)   
   地址                          数值
                       地址  34      35     36   37
 
   ptr2指向地址 0018ff35 
     所以  *ptr2 数值为 00 00 00 02(小端)
                                所以 16进制打印出来 就是 02 00 00 00 即2000000
42.
#include<stdio.h>
int main()

{

char a[1000];

int i;

for(i=0; i<1000; i++)

{

a[i] = -1-i;

}

printf("%d",strlen(a));

return 0;

}
答案是255。别惊讶,我们先分析分析。

for循环内,当i的值为0时,a[0]的值为-1。关键就是-1在内存里面如何存储。我们知道在计算机系统中,数值一律用补码来表示(存储)。主要原因是使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。正数的补码与其原码一致;负数的

补码:符号位为1,其余位为该数绝对值的原码按位取反,然后整个数加1。按照负数补码的规则,可以知道-1的补码为0xff,-2的补码为0xfe……当i的值为127时,a[127]的值为-128,而-128是char类型数据能表示的最小的负数。当i继续增加,a[128]的值肯定不能是-129。因为这时候发生了溢出,-129需要9位才能存储下来,而char类型

数据只有8位,所以最高位被丢弃。剩下的8位是原来9位补码的低8位的值,即0x7f。当i继续增加到255的时候,-256的补码的低8位为0。然后当i增加到256时,-257的补码的低8位全为1,即低八位的补码为0xff,如此又开始一轮新的循环……按照上面的分析,a[0]到a[254]里面的值都不为0,而a[255]的值为0。strlen函数是计

算字符串长度的,并不包含字符串最后的‘\0’。而判断一个字符串是否结束的标志就是看是否遇到‘\0’。如果遇到‘\0’,则认为本字符串结束。分析到这里,strlen(a)的值为255应该完全能理解了。这个问题的关键就是要明白char类型默认情况下是有符号的,其表示的值的范围为[-128,127],超出这个范围的值会产生溢出。另外还要清楚的就是负数的补码怎么表示。弄明白了这两点,这个问题其实就很简单了

43.位段
#include <stdio.h>
#include <stddef.h>

struct AA
{
 int a:8;  //int a 占用8个bit
 int b:8;
 int c:8;//
unsigned int d:8; 
};

struct BB
{
 char a:2;     //这个char 类型的变量占用2个bit,2+3+5大于8个bit
 char b:3;//1
 char c:5;//1    给再分配一个字节
 unsigned char d:7;//1   在分配一个字节
};
int main()
{
 struct AA aa;
 aa.a = 1;
 aa.b=2;
 aa.c=3;
 aa.d=4;                            4
 printf("%d\n",sizeof(struct AA));
 printf("%d\n",sizeof(struct BB)); 3
 return 0;
}
struct  AA 内部存储

  0018FF44     01 02 03 04
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: