内存分配函数
- memcpy(dst, src, size)可以将源目标的指定内存大小复制到目标地址,注意仅仅是复制,并不会为目标地址开辟内存空间,需要为目标地址开辟内存空间才行
//会发生segmention fault 11, 因为dst是一个野指针,未指向任何地址空间
int main() {
char *src = "hello\0";
char *dst;
//fix: char *dst = (char*)calloc(strlen(src), sizeof(char));
memcpy(dst, src, sizeof(char)*strlen(src));
return 0;
}
结构体和typedef
struct Person{
char *name;
int age;
};
void doWork() {
struct Person *p = (struct Person*)malloc(sizeof(struct Person));
p->name = "hello\0";
printf("%s\n", p->name);
}
typedef struct{
char *name;
int age;
}Person;
void doWork() {
Person *p = (Person*)malloc(sizeof(Person));
p->name = "hello\0";
printf("%s\n", p->name);
}
函数返回指针
//注意不要返回局部变量的指针
typedef struct{
char *name;
int age;
}Person;
Person* doWork() {
// Person *p = (Person*)malloc(sizeof(Person));
Person tmp;
Person *p = &tmp;
p->name = "hello\0";
printf("%s\n", p->name);
return p;
}
int main() {
Person *p = doWork();
printf("hellfjiowefew\n"); //如果不要这一句,可能还是正常打印,因为改部分内存可能还没回收
printf("%s\n", p->name);
return 0;
}
free释放内存
//free不会改变传入的指针值,释放后有可能还能访问到该内存数据
Person* doWork() {
Person *p = (Person*)malloc(sizeof(Person));
p->name = "hello\0";
printf("%s\n", p->name);
free(p);
printf("%p\n", p);
return p;
}
进程和线程
pid_t fork();
/*
1. 创建一个子进程, 子进程的代码和堆、打开的文件描述符都是和父进程一样的, pid, ppid不一样
2. fork方法会有两个返回值, pid_t < 0代表创建失败, 若pid_t=0代表是子进程,
pid_t!=0代表是父进程返回
*/
int execve(const char *path, char const *arg[], char const *envp[]);
/*
1. 创建进程后用于执行不同的代码段, 该函数有很多变种, 同属于exec族
2. v代表包含argv参数数组, e代表可以传递环境变量数据, l代表可以传递不定参数(execl)
3. 注意path是可执行的文件, 不能是目录, arg第一个参数是可执行文件名, arg和envp最后一个元素都是
NULL
*/
//获取进程和父进程id
pid_t getpid();
pid_t getppid();
//创建线程
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_
routine)(void*), void *arg);
//进程管道通信, 返回读写fd
#include <unistd.h>
int pipe(int fildes[2]);
void start();
int main() { pthread_t tid; int res = pthread_create(&tid, NULL, (void *) start, NULL); if(res < 0) { perror(“pthread_create”); exit(1); } pthread_join(tid, NULL); return 0; }
void start(){ for(int i = 0; i < 5; i++){ printf(“thread %u, %d\n”, pthread_self(), i); } }
<a name="CvpQ9"></a>
#### IO操作
```c
//读写
#include <unistd.h>
#include <sys/types.h>
int open(const char *path, int oflag);//打开文件, oflag是以或的形式组成的标识符
int read(int fd, void *buf, size_t nbytes);
int write(int fd, void *buf, size_t nbytes);
//socket读写
#include <sys/socket.h>
ssize_t send(int socket, const void *buf, size_t length, int flags);
ssize_t recv(int socket, void *buf, size_t length, int flags);
//fd复制, dup是在fd表最后一行创建一个新的fd, 指向fildes打开的文件; dup2是将fildes2这个fd
//指向fildes这个fd所指向的文件, 相当于关闭fildes2, 把它重定向到fildes
#include <unistd.h>
int dup(int fildes);
int dup2(int fildes, int fildes2);
//socket操作
#include <sys/socket.h>
int socket(int domain, int type, int protocol);//返回fd
//将fd绑定到指定端口和ip, sockaddr一般不会传这个类型, 而是scoketaddr_in这个, 它其实是把
//sockaddr的第二个字段拆的更细,传入是需要强转, address_len是sockadddr的长度
int bind(int socket, const struct sockaddr *address, socketlen_t address_len);
int listen(int socket, int backlog);
//获取链接, address是传出参数, 用于获取链接的ip等信息, 返回值是链接的fd
int accept(int socket, struct sockaddr *address, socklen_t address_len);
cmd
#查看linux c数据结构定义
gcc -v #查看c文件所在路径, macos是 /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr
grep -Rn -C 10 -h --include="*.h" --include="*.c" '_opaque_pthread_t {' ./
#-C是现实前后n行, -h结果不显示文件名, 一般数据结构是typedef .* pthread或者struct pthread {
#这样来查
nc -v ip port #建立tcp链接
select
- 注意每次都需要重新传入fd_set,因为select结束后,传入的fd_set会被重置成就绪的fd
- 第一个参数是最大的fd+1
- fd_set的结构是一个long类型数组,每个bit代表一个fd
```c
include
include
include
include
include
include
int main() { fd_set fds; struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; FD_ZERO(&fds); FD_SET(0, &fds); FD_SET(1, &fds); int err = select(1, &fds, NULL, NULL, &tv); if(err < 0){ perror(“select”); exit(1); } else if(err == 0){ printf(“chaoshi\n”); } else { printf(“ok\n”); } printf(“isSet %d\n”, FD_ISSET(0, &fds)); return 0; } ```
学习内容
- 进程间通信:mmap,fifo,信号量(父子进程信号量),信号
- nio
- 线程,并发