C的几个小程序和概念(also,Pointers on C)

C的几个小程序和概念(also,Pointers on C)

1、char *strncpy(char *dest, const char *src,int count)

string.h

将字符串src中的count个字符拷贝到字符串dest中去

dest 等于src的前count个字符内容

返回指向dest的指针

int main()

{

char *s2="hello";

char s1[12];

strncpy(s1,s2,sizeof(s2)+1);

//s1[sizeof(s2)+1]='\0';

cout<<s2<<" "<<s1;

}

注意:如strlen(src)的值小于count,则用dest就用NUL填充到count长度,如果strlen(src)的值大于或等于count,则只拷贝count个字符,不会以NUL字符结尾。

char *strncat(char *dest, const char *src, size_t maxlen)

将字符串src中前maxlen个字符连接到dest中

strncat则总是在结果字符串后面添加一个NUL字节。

FILE *stdin;

FILE *stdout;

FILE *stderr;

2、如何检测链表中的循环

  对访问过的元素作标识

  访问过的元素存储于一个数组中,将检测的新元素与数组中元素比较

  假设循环位于前N个元素之中,用两个指针,用一个指针定位(开始指向开始的第一个元素),用另一个指针作比较(到N-1个元素),然后第一个指针后移一个元素,第二个指针再循环比较N-2个元素。如果所有N个元素两个不等,说明链表中不存在循环。

  链表任意长:首先排除两种情况,2个元素和3个元素的情况。当有3元素时,用两个指针p1,p2,第一个指向第一个,第二个指向第三个来个判断。如果不等,则假设>=3个元素,p1后移一个元素,p2后移2个元素,...直到两个指针都为NULL的情况说明没有循环,否则其中一个指针可以追上另一个指针,也许在几遍以后。

3、左值

int x=1;

x++=2;//是不对的

因为在C/C++中,左值是定位一个对象的表达式的编译器用语,通常是一个特定的地址,也可以是一个寄存器或地址,或寄存器加上一个位段。而x++是x的值自加1,而加完后,往那里放还不知道,也就是地址不明确,所以编译不通过。

4、库函数调用和系统调用区别

库函数调用是语言或应用程序的一部分,而系统调用是操作系统的一部分。系统调用是在操作系统发现一个"trap"或中断后进行的。

6、文件描述符和文件指针

文件描述符是开放文件的每个进程表的一个偏移量,用于UNIX系统调用中,用于标识文件。

FILE指针保存了一个FILE结构的地址,FILE结构用于表示开放的I/O流。用于ANSI标准的I/O库调用中,用于标识文件。

7、确定一个变量是有符号数还是无符号数

不能用函数实现,因为函数形式参数的类型是在函数内部定义的,所以它无法穿越调用这一关。我们用宏。

无符号数的本质是永远不会是负数。有符号数的本质是对左边一个位取反将会改变它的符号。

#define ISUNNSIGNED(a)  (a>=0 && ~a>=0)

int main()

{

 unsigned int a=12;

 cout<<boolalpha<<ISUNNSIGNED(a);

}

如果宏的参数是一个类型,一个方法是使用类型转换

#define ISUNNSIGNED(type)  ((type)0-1>0)

int main()

{

 //unsigned int a=12;

 cout<<boolalpha<<ISUNNSIGNED(int);

}

8、打印一棵二叉树的值时间复杂度是多少?

在一棵二叉树中,所有的操作的时间复杂度为O(log(n)),但是要打印的话,要逐个说,所以是O(N)。

9、从文件中随即提取一个字符串

法1:记录每个字符串的偏移位置

法2:不能保存偏移位置 

则保存第一个字符串,继续读入下一个字符串,在两个字符串当中舍弃一个,继续如此处理。

10、因为当链接程序时,还无法判断自动变量的存储位置,所以没有缺省值。事实上,函数的局部变量在函数的每次调用中可能占据不同的位置。

在程序中使用字符串常量会生成一个指向字符的常量指针。

三个地方存储变量:普通内存,堆栈,寄存器。代码块外声明的变量位于静态存储区,也就是不属于堆栈的内存,称为静态变量(在函数执行之前创建);代码块内声明的变量的缺省存储类型是auto的,位于堆栈中。函数的形式参数不能声明为static,因为实参总是在堆栈中传递函数,而编译器一般用堆栈来实现递归。static变量是在链接阶段完成赋值的。

11、字符串数组

#include "iostream"

using namespace std;

int main()

{

char *str="hello";

//str[2]='e';//运行会出问题,常量字符串不能修改

//cout<<str[2];//运行会出问题

char str1[]={"hello"};//与下面的定义等价,可以修改

str1[2]='e';

cout<<str1[2];

char str2[]={'h','e','l','l','o'};

str2[2]='e';

cout<<str2[2];

char *str3[]={"Iam","you"};//字符串常量,不可修改

cout<<str3[0]<<str3[1];

//str3[0][1]='b';//字符串常量,不可修改,运行会出问题

//cout<<str3[0];

//char str4[]={"hello","metoo"};//error,初始值设定项太多

}

由上面测试,可以看出,只要是将字符串常量赋值给指针的(将字符串常量的首地址),其都不可以修改。

12、注意,strlen返回类型是size_t,是无符号类型。所以

if(strlen(s)>=10) ...//right

if(strlen(s)-10>=0) ...//wrong

13、位段和联合

声明同结构体类似。位段成员必须为int,signed int,unsigned int类型。在成员后面加一个冒号和一个整数,这个整数指定该位段所占用的位的数目。指定位段长度为0,则强制下一个位段从下一个最小位段分配单位开始存储。16位编译器最小位段为一个字符,32位为4个字节。位段是不可移植的。

联合可以被初始化,但这个初始化必须是联合第1个成员的类型,且必须位于一对花括号里面。

union{

 int a;

 float b;

 char c[4];

 }x={5};

14、动态数组的分配

const int N=4;

int (*p)[N];

p=(int(*)[N])malloc(2*sizeof(int[N]));

free((void*)p);

+++++++++++

int **a;

a=(int**)malloc(m*sizeof(int*))//建立m元数组指针,准备指向二维数组的各行

free((void*)a);

+++++++++++++++++

#include "iostream"

using namespace std;

const int N=4;

int (*New_Array(int M))[N];//指向数组的指针作为函数的返回值

void Del_Array(int (*q)[N]);

int main()

{

int (*p)[N];

int M;

cout<<"Input M"<<endl;

cin>>M;

//省略判断条件

p=New_Array(M);

//...

Del_Array(p);

}

int (*New_Array(int M))[N]{

int (*p)[N];

p=(int(*)[N])malloc(M*sizeof(int[N]));

return p;

}

void Del_Array(int(*p)[N]){

if(p!=NULL) free((void*)p);

}

++++++++++

#include "cmath"

#include "iostream"

using namespace std;

const int PI=3.14;

double (*fun(int k))(double){//函数指针作为函数的返回值

double (*g)(double);

switch(k){

case 1:g=sin;break;

case 2:g=cos;break;

default:g=fabs;

}//switch

return g;

}

int main()

{

double a,b;

a=fun(1)(PI/4);

b=fun(2)(PI/4);

}

15、在定义结构体时,让那些对边界要求最严格的成员首先出现,对边界要求最弱的最后出现。如果某机器的整形值长度为4字节,并且它的起始存储位置必须被4整除,则对于char和int,则应当先定义int.

typedef struct a1{

 char c1;

 int a;

 char c2;

 }a1;

int main()

{

 a1 aa;

 cout<<sizeof(aa);//12个字节,第一个c1只占一个字节,但是由于int型要求必须存储在起始位置为4的倍数的地方,所以有3个字节浪费。

}

typedef struct a1{

 int a;

 char c1;

 char c2;

 }a1;

int main()

{

 a1 aa;

 cout<<sizeof(aa);//8,这样浪费了2个字节,而上面的那种情况浪费了6个字节。

}

16、几个指针

int f()[]//f是一个函数,返回一个整形数组

int f[]()//f是一个数组,它的元素类型是返回值为整形的函数

int (*f[])()//f是一个数组,存储函数指针,所指向的函数的返回值为int.

unix下cdecl程序可以执行翻译。

转换表就是一个函数指针数组。

double add(double,double);

double sub(double,double);

double (*oper_func[])(double,double)={add,sub};

double result=oper_func[0/2](1.1,2.1);

17、字符串常量

当一个字符串常量出现于表达式中时,它的值是个指针常量。字符串常量的类型是指向字符的指针。

"xyz"+1//等价于char *p="xyz",p[1]

remainder=value%16;//将二进制值转为16进制字符

if(remainder<10) putchar(remainder+'0');

else putchar(remainder-10+'A');

将无符号整数转换为字符:

void

binary_to_ascii(unsigned int value){

unsigned int quotient;

quotient=value/10;

if(quotient!=0)

   binary_to_ascii(quotient);

putchar(value%10+'0');

将二进制转换为字符

int value=14;

int i=value%16;

cout<<putchar("0123456789ABCDEF"[i]);

18、预处理器符号

宏不可以出现在递归中。

常量表达式:字面值常量(1,2,...),或由#define定义的符号。

#define MAX(a,b)  ((a)>(b)?(a):(b))

using namespace std;

int main()

{

 a=MAX(x++,y++)//it's equal to ((x++)>(y++)?(x++):(y++));

}

    如果一个现存的宏需要被重定义,那么它的旧定义首先必须用#undef移除。

19、输入输出函数

标准库函数在一个外部整形变量errno中保存错误代码。

void perror( const char *string );

For accurate results, call perror immediately after a library routine returns with an error. Otherwise, subsequent calls can overwrite the errno value.

perror( "perror says open failed" );//perror says open failed: No such file or directory

刷新流

fflush(stdout);

20、与时间相关的函数

Gets the system time.

time_t time( time_t *timer );//存储在timer给定的位置

The return value is stored in the location given by timer. This parameter may be NULL, in which case the return value is not stored.

时间和日期的转换

与时间相关的函数:

http://blog.163.com/zhoumhan_0351/blog/static/399542272009918500688/

char s[]="helllo";

puts(s);

printf(s);

printf("hello");

21、信号

    信号表示一种事件,它可能异步地发生。

void ( *signal( int sig, void (__cdecl *func) ( int sig [, int subcode ] )) ) ( int sig );

前几个是同步信号,后两个是异步的。如果信号是异步的,也就是说不是由于调用abort或raise函数引起的,信号处理函数便不应调用除signal之外的任何函数。

abort不正常终止一个程序,引发SIGABRT信号

exit用于正常终止程序

当exit被调用时,所有被atexit函数注册为退出函数的函数将按照它们所注册的顺序被反序依次调用。然后,用于流的缓冲区被刷新,所有打开的文件被关闭,用tmpfile函数创建的文件被删除,然后,退出状态返回给宿主环境,程序停止执行。

一个在信息处理函数中可能修改的变量应当声明为volatile,以防止编译器优化代码,产生未预期的结果。

char *getenv( const char *varname );

int system( const char *command );

The system function passes command to the command interpreter, which executes the string as an operating-system command. system refers to the COMSPEC and PATH environment variables that locate the command-interpreter file (the file named CMD.EXE in Windows NT). If command is NULL, the function simply checks to see whether the command interpreter exists.

You must explicitly flush (using fflush or _flushall) or close any stream before calling system.

#include <process.h>

void main( void )

{

 system( "type C:\\BOOTEX.*" );

}

22、排序和查找

void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );

void *bsearch( const void *key, const void *base, size_t num, size_t width, int ( __cdecl *compare ) ( const void *elem1, const void *elem2 ) );

用二分查找。

23、longjmp不能返回到一个已经不再处于活动状态的函数。

从异步信号的处理函数中调用exit,abort函数是不安全的。

当每次信号发生时,必须重新设置信号处理函数。

避免exit函数的多重调用。

24、C模拟泛型

定义成#define宏

存储成void*类型的值,使用强制类型转换把参数的类型转换成void*等相关操作。

25、是链接器,而不是编译器决定外部标识符的最大长度。

使用stdarg实现可变参数列表。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值