第六夜:内存管理、读写文件、管道

虽然是七夕,也还是要坚持学习呀!(老婆还没下班)

内存管理

C 语言为内存的分配和管理提供了几个函数,可以在 <stdlib.h> 头文件中找到。

  • void *calloc(int num, int size)
    • 在内存中动态地分配 num 个长度为 size 的连续空间(num*size个byte),并将每一个字节都初始化为 0。
  • void free(void *address)
    • 该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。
  • void *malloc(int num)
    • 堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。
  • void *realloc(void *address, int newsize)
    • 该函数重新分配内存,把内存扩展到 newsize。

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
char name[100];
char *description;
strcpy(name, "Zara Ali");

/* 动态分配内存 */
description = (char *)malloc( 30 * sizeof(char) );
if( description == NULL ){
fprintf(stderr, "Error - unable to allocate required memory\n");
}else{
strcpy( description, "Zara ali a DPS student.");
}
/* 重新分配内存 */
description = (char *) realloc( description, 100 * sizeof(char) );
if( description == NULL ){
fprintf(stderr, "Error - unable to allocate required memory\n");
}else{
strcat( description, "She is in class 10th");
}

printf("Name = %s\n", name );
printf("Description: %s\n", description );

/* 使用 free() 函数释放内存 */
free(description);
}

读写文件

读文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>

int main(){
FILE *fp = NULL;
char buff[255];

fp = fopen("/tmp/test.txt", "r");
/*
/tmp/test.txt
This is testing for fprintf...
This is testing for fputs...
*/
fscanf(fp, "%s", buff); //fscanf() 方法只读取This,因为它在后边遇到了一个空格
printf("1: %s\n", buff );

//fgets() 从 fp 所指向的输入流中读取 n - 1 个字符复制到缓冲区 buf,并追加一个 null 字符来终止字符串。
fgets(buff, 255, (FILE*)fp);
printf("2: %s\n", buff );

fgets(buff, 255, (FILE*)fp);
printf("3: %s\n", buff );
fclose(fp);
}

写文件

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main(){
FILE *fp = NULL;

fp = fopen("/tmp/test.txt", "w+");
fprintf(fp, "This is testing for fprintf...\n");
fputs("This is testing for fputs...\n", fp);
fclose(fp);
}

C语言读写二进制文件(fread,fwrite)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define MAXLEN 1024

int main(){

FILE *src, *dst;
dst = fopen("/tmp/ls-copied", "wb" );
src = fopen("/bin/ls", "rb");
unsigned char buf[MAXLEN];
if( dst == NULL || src == NULL ){
fprintf(stderr, "%d\n", errno);
exit(1);
}

int rc;
while( (rc = fread(buf,sizeof(unsigned char), MAXLEN, src)) != 0 ){
fwrite( buf, sizeof( unsigned char ), rc, dst );
}

fclose(src);
fclose(dst);
return 0;
}

管道

管道是在同一台计算机上两个进程之间进行数据交换的一种机制。具有单向、先进先出、无结构的字节流等特点。管道有两端,一端用于写入数据,另一端用于读出数据。

根据管道提供的接口不同,(Linux系统下)分为命名管道无名管道

无名管道

无名管道用于在内核中建立一条有两个端的管道,一端用于读,另一端用于写。从管道写入端写入的数据可以从读出端读出。它占用两个文件描述符,不能被非血缘关系的进程共享,一般应用在父子进程中。

1
2
3
#include <unistd.h>

int pipe(int fildes[2]);/*其中fildes[0]为读而开,fildes[1]为写而开,fildes[1]的输出是fildes[0]的输入*/

一条命令协助理解Linux下的管道:cat /tmp/test.txt | grep XXX | more

命名管道
管道如果被命名,就可以在整个系统中使用。FIFO管道即为命名管道,它以一种特殊的文件类型存储于文件系统中,以供其他进程访问。

1
2
3
4
5
#include <sys/types.h>

#include <sys/stat.h>

int mkfifo(char *path,mode_t mode);

path:管道文件的路径和名称
mode:管道文件的权限,类似open函数的第三个参数,并且自带了O_CREAT 和O_EXCL选项。
成功调用mkfifo返回0,错误返回-1。
本函数只能创建一个不存在的管道文件,或者返回”文件已存在”错误。

例:pipe_read.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <fcntl.h>
#include <stdio.h>
void main(){
FILE *fp;
char buf[255];
while ( 1 ){
if ( (fp = fopen( "myfifo", "r" ) ) == NULL )
return;
fgets( buf, sizeof(buf), fp );
printf( "gets:[%s]", buf );
if ( strncmp( buf, "quit", 4 ) == 0 || strncmp( buf, "exit", 4 ) == 0 )
break;
fclose( fp );
}
}

pipe_write.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/errno.h>

extern int errno;
void main(){
FILE *fp;
char buf[255];
/*创建管道,如果已存在则跳过*/
if ( mkfifo( "myfifo", S_IFIFO | 0666 ) < 0 && errno != EEXIST )
return;
while ( 1 ){
if ( (fp = fopen( "myfifo", "w" ) ) == NULL )
return; /*打开管道*/
printf( "please input:" );
gets( buf );
fputs( buf, fp );
fputs( "/n", fp );
if ( strncmp( buf, "quit", 4 ) == 0 || strncmp( buf, "exit", 4 ) == 0)
break;
fclose( fp );
}
}

Windows下的管道

Windows下的C语言管道示例link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

int runcmd( char* lpCmd )
{
char buf[2048] = {0}; //缓冲区
DWORD len;
HANDLE hRead, hWrite; // 管道读写句柄
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;

//ZeroMemory( buf, 2047 );
sa.nLength = sizeof( sa );
sa.bInheritHandle = TRUE; // 管道句柄是可被继承的
sa.lpSecurityDescriptor = NULL;

// 创建匿名管道,管道句柄是可被继承的
if( !CreatePipe( &hRead, &hWrite, &sa, 2048 ) )
{
printf( "管道创建失败!(%#X)\n", (unsigned int)GetLastError() );
return 1;
}

ZeroMemory( &si, sizeof( si ) );
si.cb = sizeof( si );
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdError = hWrite;
si.hStdOutput = hWrite;

// 创建子进程,运行命令,子进程是可继承的
if ( !CreateProcess( NULL, lpCmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ) )
{
printf( "创建进程失败!(%#x)\n", (unsigned int)GetLastError() );
CloseHandle( hRead );
CloseHandle( hWrite );
return 1;
}
// 写端句柄已被继承,本地需要关闭,不然读管道时将被阻塞
CloseHandle( hWrite );
// 读管道内容,并显示
while ( ReadFile( hRead, buf, 2047, &len, NULL ) )
{
printf( buf );
ZeroMemory( buf, 2047 );
}
CloseHandle( hRead );
return 0;
}

int main( int argc, char** argv )
{
char cmd[256];
printf( "输入命令行:" );
gets( cmd );
runcmd( cmd );
system( "pause" );
return 0;
}
第七夜:线程、多线程 第五夜:C语言标准库、错误处理和调试、输入输出
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×