站长原创,版权所有ITEEDU,2011-06-24
笔者于2011年6月26日就得去公司工作,在休息的两天中,闲着无聊,就整理了一些在linux中C语言开发时常遇到的问题,并做以记录。
如果函数的参数是一个指针,不要指望用该指针去申请动态内存。示例1-1中,Test函数
的语句GetMemory(str, 100)并没有使str获得期望的内存,str依旧是NULL,为什么?
void GetMemory(char *p, int num)示例1-1 试图用指针参数申请动态内存
{
p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char *str = NULL;
GetMemory(str, 100); // str 仍然为 NULL
strcpy(str, "hello"); // 运行错误
}
void GetMemory2(char **p, int num)示例1-2用指向指针的指针申请动态内存
{
*p = (char *)malloc(sizeof(char) * num);
}
void Test2(void)
{
char *str = NULL;
GetMemory2(&str, 100); // 注意参数是 &str,而不是str
strcpy(str, "hello");
cout<< str << endl;
free(str);
}
char *GetMemory3(int num)示例1-3 用函数返回值来传递动态内存
{
char *p = (char *)malloc(sizeof(char) * num);
return p;
}
void Test3(void)
{
char *str = NULL;
str = GetMemory3(100);
strcpy(str, "hello");
cout<< str << endl;
free(str);
}
char *GetString(void)示例1-4 return语句返回指向“栈内存”的指针
{
char p[] = "hello world";
return p; // 编译器将提出警告
}
void Test4(void)
{
char *str = NULL;
str = GetString(); // str 的内容是垃圾
cout<< str << endl;
}
char *GetString2(void)示例1-5 return语句返回常量字符串
{
char *p = "hello world";
return p;
}
void Test5(void)
{
char *str = NULL;
str = GetString2();
cout<< str << endl;
}
(1)再次重申:如果函数的参数是指针,千万不要用该指针申请动态内存。
(2) exit退出整个程序;return退出所在的函数。
(3) 静态链接库的编译
ar r libmm.a mm.o//编译静态连接库,2步走
静态连接库是在编译的时候将函数库连接进程序
如何调用静态连接库:
cc -o nn nn.c -L/usr/work/lib -Bstatic -lmm
//其中-Bstatic可以省略
另外,再加上-K PIC后可以提高内存的使用率
//编译动态链接库,它是在程序启动的时候才连接需要的函数
所以利用动态链接库的程序比静态链接库要小得多
需要的环境变量 :(注意:不要忘记设)
LD_LIBRARY_PATH:增加链接程序搜索路径。
LD_RUN_PATH:指定动态链接程序的搜索路径。
例如:LD_LIBRARY_PATH=/usr/work/lib;export LD_LIBRARY_PATH
如何调用libmyfun.so:
cc -o mm mm.c -L/usr/work/lib -Bdynamic -lmyfun
LD_LIBRARY_PATH必须设置,否则运行mm的时候找不到libmyfun.so!
(4)查看动态链接库
察看程序调用了哪些动态链接库利用ldd:
ldd mm 输出入下:
dynamic linker: nn: file loaded: /usr/work/zzy/libmyfun.so
dynamic linker: nn: file loaded: /usr/lib/libc.so.1
(5) char *str1;
char *str2;
(str1==str)
//这种写法正确,它是指str1和str2指向了同一块内存,即地址相等 ...
if (strcmp(str1,str2)==0)//它是指str1和str2所指向的内存中的内容相等
(6)变量作用域
他们的程序块的每个入口多次初始化。静态局部变量也只初始化一次。
个程序。
(7)变量的存储方式:extern、register、static、auto。
函数存储默认为extern
(8)char *str="abc";
printf("%d\n",str);//打印地址
printf("%s\n",str);//打印字符串
(9)初始化数据段。通常将此段称为数据段,它包含了程序中需赋初值的变量。例如,C程序中任何函数之外的说明:
int maxcount = 99;使此变量以初值存放在初始化数据段中。
初始化数据段一般是全局变量和静态变量。
(10)非初始化数据段。通常将此段称为b s s段,这一名称来源于早期汇编程序的一个操作符,
意思是“block started by symbol(由符号开始的块)”,在程序开始执行之前,内核将此段初始化为0。函数外的说明:
long sum[1000] ;
使此变量存放在非初始化数据段中。
(11)栈。自动变量以及每次函数调用时所需保存的信息都存放在此段中。每次函数调用时,
其返回地址、以及调用者的环境信息(例如某些机器寄存器)都存放在栈中。然后,新被调
用的函数在栈上为其自动和临时变量分配存储空间。通过以这种方式使用栈,C函数可以递归调用。
(12)堆。通常在堆中进行动态存储分配。由于历史上形成的惯例,堆位于非初始化数据段顶和栈底之间。
初始化的数据----由exec 赋初值
初始化的数据------由exec从程序文件中读到
正文--------------由exec从程序文件中读到
注:平常所说的堆栈即为栈(stack)
(13) 函数名: memcpy
功 能: 从源source中拷贝n个字节到目标destin中
用 法: void *memcpy(void *destin, void *source, unsigned n);
他同strcpy的区别就是memcpy可以给结构赋值:
struct JieGou jg1;(14) char *str1="abcd";
struct JieGou jg2;
memcpy(jg1,jg2,sizeof(struct JieGou));
它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向
合法的内存。例如
char *p = NULL;
char *str = (char *) malloc(100);
TRUE定义为-1。示例程序如下:
BOOL flag;
…
if(flag) { // do something } // 正确的用法
if(flag==TRUE) { // do something } // 危险的用法
if(flag==1) { // do something } // 危险的用法
if(!flag) { // do something } // 正确的用法
if(flag==FALSE) { // do something } // 不合理的用法
if(flag==0) { // do something } // 不合理的用法
(16)以下是我编程时采用的命名约定:
对于 int, float, double 型的变量,如果变量名的含义十分明显,则不加前缀,避免烦琐。
如用于循环的int型变量 i,j,k ;float 型的三维坐标(x,y,z)等。
(17)strcpy与memcpy的区别
strcpy(str1,str2)是将str2连带'\0'一同拷贝到str1,比如:str1="aaaa",str2="bbb",
则结果str1="bbb",导致str1碰到'\0',自动终止字符串
memcpy(str1,str2,strlen(str2))是将str2的bbb(不带'\0')拷贝到str1中,结果为
str1="bbba"
这里注意:strlen(str2)=3 sizeof(str2)=4 所以如果不想覆盖str1中其余的字符,
则不能用sizeof。
strncpy与memcpy功能一样。
(18)跨平台申请内存
malloc(sizeof(char)*(strlen(str)+1))
char str[]="hello world";
char str1[9]
这两种情况sizeof(str)=12;sizeof(str1)=9;strlen(str)=11;strlen(str1)=0;
char *p=str;
sizeof (p)=4
如果将str[]用作参数例如fun(char str[100])
则sizeof(str)=4而不是100,因为此时字符串数组自动退化为指针,而指针在内存中占用4个字节。
(19)char *str = "ABCD";
str[0]='M';//将导致错误,因为这种方式是在静态存储区分配内存
如果用strcpy(str,"BBBB")会出错,因为char *str没有动态分配内存,而是指向了静态存储区。
其相对于常量字符串,是不可以修改的。
如果写成:
char *str = (char *)malloc(100)
strcpy (str,"ABCD");
str[0] = 'M';即可
char str[4] = "ABC";
str[0]='N';
则str = "NBC";因为是在栈上分配内存
(20)new 必须与delete对应,动态内存必须要手工释放,动态内存分配的析构函数只会在使用delete的时候被调用。
(21)如果文件中的记录格式为定长,长度为99,则定义:
char *strBuf[101]//99+'\n'+'\0';
fgets(strBuf,101,fp)//从fp中读取100个字符包括\r,都作为一个字符串,最后以'\r'结束(fgets中读取字符串的长度是101-1),这样输出到另一个文件的时候fprint(fp1,"%s",strBuf)自动回车,不用在%s后加\n。
如果用fread(strBuf,100,1,fp)这样的写法即可。
但是之前必须执行memset(strBuf, '\0', sizeof(strBuf)),否则会出现很乱的字符,而fgets不存在这个问题,不知道为什么。总之,在使用字符串之前,最好memset一下。
无论用fgets还是fread读取的文件的记录结果strlen(strBuf)不是99,而是100。
另外:int fread(void *buf, int size, int count,FILE *stream)表示从文件中读取count(字段数)个字段,每个字段大小为size,函数返回实际读取的字段数。如果函数要求的字段数超过实际文件存放的字段数,举例说明:
文件内容如下:
AAAAAAAAAA
AAAAABBBBB
AAAAA
while(fread(strBuf,11,1,fp))如果用fread读文件while(fread(strBuf,11,1,fp))只能读两条出来,前两条返回1,第三条返回0。因为第三条只有6个字节(连着回车符),不满足长度为11的条件,所以fread读文件的话,最实用于定长文件。但是第三条fread也将值赋给了strBuf,只是不满足条件返回了0。第三条的格式:
{
printf("str=[%s]\n",str);
}
[AAAAA(此处一个回车符)
BBBB(此处一个回车符)——这部分是上一个串留下的部分
]
[AAAAA(此处一个回车符)
]
在例如:
AAAAAAAAAA
AAAAA
AAAAAAAAAA
这种情况会跨行截取11个字节,第一条正常,第二条就变为:
[AAAAA(此处是一个回车符)
AAAAA]
如下例所示:
#include <stdio.h>运行结果:
#include <stdlib.h>
#include <memory.h>
int main()
{
int i =0;
FILE *fp,*fp1,*fp2;
char str[10]="370706751";
char str1[11];
memset(str1,'\0',sizeof(str1));//将str1用\0填充
fp = fopen("e:\\d_vim\\test.txt", "w");
if (fp == NULL)
{
printf("create file error!\n");
return -1;
}
fprintf(fp, "%s\n\0",str);
fprintf(fp, "%s\n\0",str);
fclose(fp);
fp1 = fopen("e:\\d_vim\\test.txt", "r");
if (fp1 == NULL)
{
printf("create file error!\n");
return -1;
}
fp2 = fopen("e:\\d_vim\\test1.txt", "w");
if (fp2 == NULL)
{
printf("create file error!\n");
return -1;
}
while (fgets(str1,11,fp1) )
{
i++;
printf("%s",str1);//读取test.txt中内容,放入str1中,并在终端显示出来
fprintf(fp2,"%s",str1);//将text.txt中的内容,写入text1.txt中
}
fclose(fp1);
fclose(fp2);//千万勿忘记fclose
printf("record num = [%d]\n",i);
system("pause");
}
record num = [2]
而且,在e:\d_vim\test.txt中有两行内容:
370706751
370706751
在e:\d_vim\test1.txt中有两行内容:
370706751
370706751
(22)防止野指针。
Object object1 = new Object();
......
delete object1;
object1 = NULL;
好的编程习惯,最后置为NULL,防止野指针,因为虽然内存释放了,但是指向一堆垃圾。
(23)memcpy、strncmp和strncpy与memcpy
int main(int argc , char *arge[])输出结果:--equal
{
char str[]=" aaaaaaaaa\0bbbbb";
char str1[]=" aaaaaaaaa";
if (memcmp(str,str1,17) == 0)
printf("equal\n");
if (strncmp(str,str1,17) == 0)
printf("--equal\n");
}
从上例可以看出strncmp和memcmp的区别,strncmp会截断字符串str与str1判断,所以输出相等,但是memcmp会将'\0'带入比较所以不相等。
同理,strncpy与memcpy的区别也一样。
int main(int argc , char *arge[])memcpy会输出'\0'后面的bbbbb,而strncpy不会输出。
{
char str[18]=" aaaaaaaaa\0bbbbb";
char str2[18];
strncpy(str2,str,17);
for (int i=0; i<17; i++)
{
printf("str2[%d]=[%c]\n", i, str2[i]);
}
memcpy(str2,str,17);
for (int i=0; i<17; i++)
{
printf("str2[%d]=[%c]\n", i, str2[i]);
}
}