C/C++基础知识(最终篇)

int a=0;自动变量a,再次调用时重新分配储存空间,即0
static int b=1;静态变量b,再次调用保留原值,即b不为1
//auto、register表示自动存储
  自动存储是变量的默认状态
//extern、static表示静态存储
  extern为全局说明的标识符
  static在第一次调用后,变量变为上次退出函数的值,函数退出后,系统保持其存储空间和数值
当局部变量与全局变量同名时,在块内,全局变量被屏蔽,要在块内访问全局变量,可以用作用域运算符”:: ”,全局变量说明时默认初始值为0


C语言的特性:强大的控制结构、快速、紧凑的代码——程序更小、可移植性、灵活性、面向编程人员和高效性,
缺点:太过于自由,可能会犯难以发现的编程错误。


现代计算机可分为几个部件。中央处理单元(或称CPU)担负着绝大部分的计算工作,
随机访问存储器(或称RAM)作为一个工作区来保存程序和文件;永久存储器,一般是硬盘,
即使在计算机关机时也能记下程序和文件;还有各种外围设备(如键盘,鼠标和监视器)用来提供人与计算机之间的通信。CPU负责处理程序。


PS: 为什么不内置输入输出语句
  也许你想知道为什么不自动包含像输入输出这样基本的语句。
一个答案是并非所有的程序都要用到 I/O(输入/输出)包,并且 C语言的一个基本设计原则是避免不必要的成分。
这个经济地使用资源的原则使得 C语言在嵌入式编程中非常流行,例如,为一个控制自动燃料系统的芯片编写程序。
顺便说一句,#include 甚至不是 C语言的语句!第一列中的 #号表明这一行是在编译器接手之前由 C预处理处理的语句。
以后你将碰到更多预处理器指令的例子,第16章“C预处理器和C库”将对它们做更详细的讲解。


PS: 位,字节和字
  术语:位,字节和字 用于描述计算机数据单位或机存储单位。这里主要指存储单位。
   最小的存储单位称为 位(bit)。它可以容纳两个值(0或1)之一(或者可以称该位置被置为“关”或“开”)。
不能在一个位中存储更多的信息,但是计算机中包含数量极其众多的位。位是计算机存储的基本单位。
   

字节(Byte)是常用的计算机存储单位。几乎对于所有的机器,1个字节均为 8位。
这是字节的标准定义,至少在衡量存储单位时是这样(C语言中对此有不同的定义,请参见本章“使用字符:char类型”小节)。
由于每个位或者是0 或者是1,所以一个 8位的字节包含 256(2 的 8次方)种可能的 0,1组合。
这些组合中可以用于表示 0 到 255的整数或者一组字符。
这种表示可以通过二进制编码(仅用0或1 方便地表示数字)来实现(第15章“位操作”将讨论二进制编码)。
  对于一种给定的计算机设计,字(word)是自然的存储单位。对于8位微机,比如原始的 Apple机,一个字正好有8位。
使用80286处理器的早期 IBM兼容机是 16位机,这意味着一个字的大小为 16位。
基于 Pentium 的 PC机和 Macintosh PowerPC 中的字是32位。更强大的计算机可以有 64位甚至更长位数的字。


六. 显示八进制数和十六进制数
  C既允许你使用 3种数制书写数字也允许以这 3种数制显示数字。要用八进制而不是十进制显示数字,请用%o (字母o)。
要显示十六进制整数,请使用 %x 。如果想显示 C语言前缀,可以使用说明符 %#o, %#x 和 %#X 分别生成 0,0x,0X 前缀。


终止程序执行:
abort(void);    需要头文件stdlib.h
cassert(a);   判断a的值,若为false,则终止程序执行,需要头文件cassert
exit(status);  status为整型常量,终止程序时把它作为退出代码操作系统,通常为0或1,可省略,需要头文件stdlib.h


头文件stdlib.h调用随机函数,time.h把日期和时间转换为字符串,
srand(int(time(0)));//srand每次运行程序产生的随机数是固定的,但srand(int(time(0)))表示不固定。
time(0)为函数,0表示不存储秒数,以当前时间对应int值为随机序列起点,每次运行时,由于起点不同可得到不同的随机数


char weight; char name[40];请注意,在scanf()中,weight使用了&前缀,而name却没有使用(正如你所见,&weight和name都是地址)
对于scanf(“%s”,&name)的输入:Hilary Bubbles,sacnf只读取了Hilary Bubbles的名字Hilary。
scanf()开始读取输入以后,会在遇到的第一个空白字符空格(blank)、制表符(tab)或者换行符(newline)处停止读取。
因此,读到Hilary Bubbles中的空格时,就停止了扫描。一般情况下,使用%s的scanf()只会把一个单词而不是整个语句作为字符串读入。
C使用其他读取输入函数(例如gets())来处理一般的字符串。


Sizeof运算符提供的数目比strlen()大1,这是因为它把用来标志字符串结束的不可见的空字符也计算在内。
还有一点,sizeof是否使用圆括号取决于是想获取一个类型的大小还是想获取某个具体量的大小。
圆括号对于类型是必需的,而对于具体量则是可选的。也就是说,应该使用sizeof(char)或sizeof(float),
但是可以使用sizeof name或者是sizeof 6.28。不过在所有情况下都使用圆括号会更好,例如sizeof(6.28)。
char str[20]="0123456789";
int a=strlen(str); //a=10;
int b=sizeof(str); //b=20;


printf()函数也有一个返回值可作参数传递,它返回所打印的字符的数目(包括空格和不可见的换行字符如\n算两个)。
如果有输出错误,那么printf()会返回一个负数。
printf()里的输出太多,一行放不下的解决方法:
1、     使用多个printf()语句
printf(“HERE’S ONE WAY TO PRINT A ”);
printf(“LONG STRING.\N”);
2、     用反斜杠符号和回车键组合来结束第一行,但下一行要紧接在最左边
printf(“HERE’S ONE WAY TO PRINT A \
LONG STRING.\N”);
3、     采用字符串连接的方法,仅用空白字符分隔
printf(“HERE’S ONE WAY TO PRINT A “
“LONG STRING.\N”);



~scanfscanf("")!=EOF效果一样

scanf()函数允许把普通字符放在格式字符串中。除了空格字符之外的普通字符一定要与输入字符串准确匹配。
例如,如果无意间把逗号放在两个说明之间:
scanf(“%d, %d”,&m,&n);
scanf()函数将其解释成,你将键入一个数字,键入一个逗号,然后再键入一个数字。
scanf()函数返回成功读入的项目的个数,
如果它没有读取任何项目(当它期望一个数字而你却键入了一个非数字字符串时就会发生这种情况),scanf()会返回0。
*在scanf和printf中可跳过一个项目,例如:
(%*d,&m,&n)则m不操作,%d作用于n。
ch=getchar();   等同于   scanf(“%c”,&ch);
putchar(ch);   等同于   printf(“%c”,ch);
getche()用于回显的或不回显的非缓冲输入,该函数需要调用头文件conio.h
例如:char a;                        char a;
          a=getche();                  a=getchar();
          puts("");                     puts("");
          putchar(a);                  putchar(a);
putche()函数在键入1之后,想继续键入时,它就直接开始输出了,没法像putchar()那样键入到缓冲区,而且没换行


指定初始化(需要C99版本才可使用)
  int a[6]={[5]=212};
等同于int a[6]={0,0,0,0,0,212};
一种错误:对未初始化的指针取值
int *pt;
*pt=5;
第二行表示把数值5存储在pt所指向的地址。但是由于pt没有被初始化,因此它的值是随机的,不知道5被存储到什么位置。
这个位置也许对系统危害不大,但也许会覆盖程序数据或代码,甚至导致程序的崩溃。
切记:当创建一个指针时,系统只分配了用来存储指针本身的内存空间,并不分配用来存储数据的内存空间。
因此在使用指针之前,必须给它赋予一个已分配的内存地址。
比如,可以把一个已存在的变量地址赋给指针(当你使用带有一个指针参量的函数时,就属于这种情况)。
或者使用函数malloc()来首先分配内存。
例如:int *pt;
          pt=(int*)malloc(sizeof(int));
     *pt=5;
      printf("%d\n",*pt); //若不带*号,输出为地址
指向多维数组的指针:
int (*pz)[2];  //pt指向一个包含2个int值的数组
该语句表明pz是指向包含两个int值得数组的指针
int *pax[2]; 
首先方括号和pax结合,表示pax是包含两个某种元素的数组。然后和*结合,表示pax是两个指针组成的数组。
最后,用int来定义,表示pax是由两个指向int值的指针构成的数组。这种声明会创建两个指向单个int值的指针。


gets()函数从系统的标准输入设备(通常键盘)获得一个字符串,没有预定的长度,
所以gets()需要知道输入何时结束。解决方法是读字符串直到遇到一个换行字符(\n),按回车键可以产生这个字符。
它读取换行符之前(不包括换行符)的所有字符,在这些字符后添加一个空字符(\0),然后把这个字符串交给调用它的程序。
gets()函数有两个可能的返回值。如果一切都顺利,它返回的是读入字符串的地址。
如果出错或者遇到文件结尾,它就返回一个空(或0)地址,称为空指针(NULL)。
可用while(gets(name)!=NULL)来输入
gets()的一个不足是它不检查预留存储区是否能够容纳实际输入的数据。多出来的字符简单溢出到相邻的内存区。
fgets()改进了这个问题,fgets(name,MAX,stdin);第二个参数为说明最大读入字符数,stdin(standard input)从键盘上读数据。
另外,fgets还存储了换行符到字符串中,这样每次显示字符串时就会显示换行符。
scanf()函数若使用了指定字段宽度的字符串输入,例如%10s,scanf()就会读入10个字符或者直到遇到第一个空白字符。
puts()显示字符串时自动在其后添加一个换行符,所以printf(“\n”)等同于puts(“”)
要在一行里显示多个不同字符,最好使用printf()。
puts()遇到空字符时就会停下来,所以应该确保有空字符的存在。同样为了改进,fputs(line,stdout)并不为输出自动添加换行符。
putchar(*string++);记住,++比*的优先级高,这意味着putchar(*string++)输出string的值,
然后再增加string本身,而不是增加string指向的字符。
使用const char *string代替const char string[]时,*string的实参不一定是数组。
strncat(string1,string2,MAX);由于strcat并不检查第一个字符串是否能容纳第二个字符串,所以strncat进行了改进。
同样,strncmp(list[i],”astro”,5)可以限定搜索前5个字符。还有strncpy()需要添加可复制的最大字符数。
sprint()可以把几个元素组合成一个字符串,sprintf()的第一个参数是目标字符串地址,其余的参数和printf()一样。
它写到字符串里,而不会写到输出显示中。
例如:sprintf(string1,”%s,%-19s:$%6.2f\n”,a,b,c);
char *strchr(const char *s,int c);
该函数返回一个指向字符串s中存放字符c的第一个位置的指针(标志结束的空字符是字符串的一部分,因此也可以搜索到它)。
如果没找到该字符,函数就会返回空字符。
char *strpbrk(const char *s1,const char *s2);
该函数返回一个指针,指向字符串s1中存放s2字符串中任何一个字符的第一个位置。如果没找到任何字符,函数就返回空指针。
char *strrchr(const char *s,int c);
该函数返回一个指针,指向字符串s中字符c最后一次出现的地方(标志结束的空字符是字符串的一部分,因此也可以搜索到它)。
如果没找到该字符,函数就会返回空字符。
char *strstr(const char *s1,const char *s2);
该函数返回一个指针,指向s1字符串中第一次出现s2字符串的地方。如果在s1中没找到s2字符串,函数就返回空指针。

main()可以没有参数,或者有两个参数(有些更多)。有两个参数时,第一个参数是命令行中的字符串数。
按照惯例(但不是必须的),这个int参数被称为argc(代表argument count)。
系统使用空格判断一个字符串结束,另一个字符串开始。第二个参数是一个指向字符串的指针数组。
命令行中的每个字符串被存储在内存中,并且分配一个指针指向它。按照惯例(但不是必须的),这个指针数组被称为argv(代表argument value)。

头文件<stdlib.h>
atoi()函数(代表alphanumeric to integer),如果数字是整型,可以把字符串转换成整型。
例如:atoi(“42regular”)返回整数42
同样还有atof()转成double,atol()转成long
还有比较复杂的strtol()转成long,strtod()转成double,
strtoul()转成unsigned long
当然也有itoa()和ftoa()函数把整数和浮点数转换成字符串,如果要求兼容性更好,可以使用sprintf函数来完成。

volatile的意思是告诉编译器,在编程源代码时,对这个变量不要使用优化。
在一般的程序设计中,如:
int *a;
return (*a) * (*a);这种情况。
通常编译器为了减少存储器的读写时间,会把代码优化为:
int *a; int b; int c;
c = *a;

b=*a;
return b * c;
因为外部存储器的读写速度肯定赶不上内存的读写速度,这样可以省一次外部存储器的读取时间,从而提高速度。
如果把int *a改为volatile int* a编译器就不会自动把它优化掉了。
在整个运算过程中,对变量*a的值读取了两次。防止因变量*a的值在这一期间发生了改变,而导致程序结果的错误。

restrict

      restrictc99引入的,它只可以用于限定指针,并表明指针是访问一个数据对象的唯一且初始的方式,考虑下面的例子:

1

2

3

int ar[10];

扫描二维码关注公众号,回复: 1909159 查看本文章

int * restrict restar=(int *)malloc(10*sizeof(int));

int *par=ar;

这里说明restar是访问由malloc()分配的内存的唯一且初始的方式。par就不是了。那么:

1

2

3

4

5

6

7

8

for(n=0;n<10;n++)

{

   par[n]+=5;

   restar[n]+=5;

   ar[n]*=2;

   par[n]+=3;

   restar[n]+=3;

}

因为restar是访问分配的内存的唯一且初始的方式,那么编译器可以将上述对restar的操作进行优化:restar[n]+=8;

par并不是访问数组ar的唯一方式,因此并不能进行下面的优化:par[n]+=8;

因为在par[n]+=3前,ar[n]*=2进行了改变。使用了关键字restric,编译器就可以放心地进行优化了。

这个关键字据说来源于古老的FORTRAN

寄存器变量:例:register int i;
通常变量存储在计算机内存中,如果幸运,寄存器变量可以被存储在CPU寄存器中,
或更一般的,存储在速度最快的可用内存中,从而比普通变量更快地被访问和操作。
因为寄存器变量多是存放在一个寄存器中而非内存中,所以无法获得寄存器变量的地址。
通过extern可以获知外部变量


函数malloc()接受一个参数:所需内存字节数。然后malloc找到可用内存中一个大小合适的块。
内存是匿名的,也就是说,它分配了内存,但没有为它指定名字。然而,它却可以返回那块内存第一个字节的地址。
因此,可以把那个地址赋值给一个指针变量,并使用该指针来访问那块内存。若找不到所需的内存,它将返回空指针。
函数free()释放malloc分配的内存,它只释放它的参数所指向的内存块。使用它并不是必须的,
因为在程序终止后所有已分配的内存都将被自动释放。但对于动态变量十分重要,否则容易造成内存溢出而泄露。
函数calloc()与malloc类似,它接受两个参数,第一个是所需内存单元的数量,第二个是每个单元以字节计的大小,
例如:mem=(long*)calloc(100,sizeof(long));
fprintf(stderr, "Cancel Operating.\n");不仅可以组合字符串,而且使用stderr可以直接把组合的字符串输出到屏幕

首先, malloc是一个库函数,返回值是void *形式的,new是一个运算符,返回值类型与new的对象/变量的指针相同。

       其次,newdelete的实现实际上是调用了malloc/free的。

       最后,介绍最重要的,对于非内部类型来说, malloc是不能满足要求的,

因为malloc只是分配堆内存(不会调用构造函数),new是分配且内存且在此创建一个对象(会调用构造函数)。

所以malloc加上string会出错,因为它无法初始化,无法调用string的构造函数,因此需要改用new来使用。

 
 
用#if  0
  ………..
 #endif             的方式作注释会比较安全


数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问

未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

 

猜你喜欢

转载自blog.csdn.net/fcsfcsfcs/article/details/80388103