文章目录
1.字符串工具封装
- 字符串工具
去除\r\n,crlf:cr: carriage return回车,lf:line feed换行
void str_trim_crlf(char *str);
字符串分割,依据c字符分割,前一段保存在left,后一段保存在right
void str_split(const char *str , char *left, char *right, char c);
判断是否全是空白字符,若都是空白字符返回为真,不都是空白字符返回为0
int str_all_space(const char *str);
字符串转化为大写格式
void str_upper(char *str);
将字符串转换为long long,不能用atoi,因为atoi转换为int4个字节
long long str_to_longlong(const char *str);
将字符串(八进制)转化为十进制的无符串整型
unsigned int str_octal_to_uint(const char *str);
- eg:
str.h
#ifndef _STR_H_
#define _STR_H_
void str_trim_crlf(char *str);
void str_split(const char *str , char *left, char *right, char c);
int str_all_space(const char *str);
void str_upper(char *str);
long long str_to_longlong(const char *str);
unsigned int str_octal_to_uint(const char *str);
#endif /* _STR_H_ */
str.c
#include "str.h"
#include "common.h"
void str_trim_crlf(char *str)
{
char *p = &str[strlen(str)-1];//指向最后一个字符
while (*p == '\r' || *p == '\n')
*p-- = '\0';
}
void str_split(const char *str , char *left, char *right, char c)
{
char *p = strchr(str, c);
if (p == NULL)//说明没有参数
strcpy(left, str);
else
{
strncpy(left, str, p-str);//空格前的字符串拷贝到left
strcpy(right, p+1);//空格之后的字符串拷贝到right
}
}
int str_all_space(const char *str)
{
while (*str)
{
if (!isspace(*str))//只要一个不是空白字符就返回0
return 0;
str++;
}
return 1;//全都是空白字符返回1
}
void str_upper(char *str)
{
while (*str)
{
*str = toupper(*str);//toupper它不是通过参数*str返回回来,而是通过返回值返回回来
str++;
}
}
long long str_to_longlong(const char *str)
{
//return atoll(str);//方法1:可以直接使用atoll,但不是所有的系统都支持
/*
将一个字符串转换为整数
12345678
8*1 +
7*10 +
6*10*10+
。。。
*/
//方法2:
/* long long result = 0;
long long mult = 1;
unsigned int len = strlen(str);
unsigned int i;
if (len > 15)
return 0;
for (i=0; i<len; i++)
{
char ch = str[len-(i+1)];//最后一个字符,len-i-1,因为i从0开始
long long val;
if (ch < '0' || ch > '9')
return 0;
val = ch - '0';
val *= mult;
result += val;
mult *= 10;
}
*/
//方法3:for循环倒过来
long long result = 0;
long long mult = 1;
unsigned int len = strlen(str);
int i;//注意这里不要写成unsigned int i;一旦i=0再进行减操作,它永远不会=-1,i会变成最大数了,因为i是无符号int,所以for循环
//会不断循环,直到越界为止,因为不在'0'-'9'之间,所以会一直返回0
if (len > 15)
return 0;
for (i=len-1; i>=0; i--)//len-1是最后一个字符
{
char ch = str[i];
long long val;
if (ch < '0' || ch > '9')
return 0;
val = ch - '0';
val *= mult;
result += val;
mult *= 10;
}
return result;
}
unsigned int str_octal_to_uint(const char *str)
{
unsigned int result = 0;
int seen_non_zero_digit = 0;
/*
方法1:与str_to_longlong类似
123456745
5*1 +
4*8 +
7*8*8 +
方法2:从左边第一个非0数开始,从高位开始计算
如果是10进制
0*10+1
1*10 + 2 = 12
12*10 + 3 =123
123*10 + 4 = 1234
这里是8进制,所以将上面的10换成8即可
0*8+1
1*8 + 2 = 12
12*8 + 3 =123
123*8 + 4 = 1234
*/
while (*str)
{
int digit = *str;//将当前的位保存至数字
if (!isdigit(digit) || digit > '7')
break;
if (digit != '0')
seen_non_zero_digit = 1;
if (seen_non_zero_digit)
{
//等价于:result = result << 3;
result <<= 3;//左移3bit就是乘以8,移位运算比直接乘以8效率高
result += (digit - '0');
}
str++;
}
return result;
}
ftpproto.h
#ifndef _PRIV_PARENT_H_
#define _PRIV_PARENT_H_
#include "session.h"
void handle_parent(session_t *sess);
#endif /* _PRIV_PARENT_H_ */
ftpproto.c
#include "ftpproto.h"
#include "sysutil.h"
#include "str.h"
void handle_child(session_t *sess)
{
//每条指令后面加上\r\n,这是ftp协议所规定的
writen(sess->ctrl_fd, "220 (miniftpd 0.1)\r\n", strlen("220 (miniftpd 0.1)\r\n"));
int ret;
while (1)
{
memset(sess->cmdline, 0, sizeof(sess->cmdline));
memset(sess->cmd, 0, sizeof(sess->cmd));
memset(sess->arg, 0, sizeof(sess->arg));
ret = readline(sess->ctrl_fd, sess->cmdline, MAX_COMMAND_LINE);
if (ret == -1)//关闭ftp服务进程,此外,还要关闭nobody进程,通过socketpair的方式,后期再加上
ERR_EXIT("readline");
else if (ret == 0)//表示客户端断开连接
exit(EXIT_SUCCESS);
//客户端连接过来的话,会发送USER jjl\r\n字符串,这里服务端将接收的字符串打印出来
//将USER jjl\r\n字符串,用空格分隔成2段
printf("cmdline=[%s]\n", sess->cmdline);
// 去除\r\n
str_trim_crlf(sess->cmdline);
printf("cmdline=[%s]\n", sess->cmdline);
// 解析FTP命令与参数
str_split(sess->cmdline, sess->cmd, sess->arg, ' ');
printf("cmd=[%s] arg=[%s]\n", sess->cmd, sess->arg);
// 将命令转换为大写
str_upper(sess->cmd);
// 处理FTP命令
}
}
main.c
#include "common.h"
#include "sysutil.h"
#include "session.h"
#include "str.h"
/*
typedef struct session
{
// 控制连接
char cmdline[MAX_COMMAND_LINE];
char cmd[MAX_COMMAND];
char arg[MAX_ARG];
// 父子进程通道
int parent_fd;
int child_fd;
} session_t;
*/
int main(void)
{
char *str1 = " a b";
char *str2 = " ";
if (str_all_space(str1))
printf("str1 all space\n");
else
printf("str1 not all space\n");
if (str_all_space(str2))
printf("str2 all space\n");
else
printf("str2 not all space\n");
//char *str3 = "abcDef"; // 指针指向一个字符串常量,常量不能被修改,调用str_upper会出错
char str3[] = "abcDef"; //定义了一个字符数组,将常量存入字符数组变量中,str3本质上是变量,只不过将常量内容搬到变量中而已
str_upper(str3);
printf("str3=%s\n", str3);
long long result = str_to_longlong("12345678901234");//一个无符号整型为4bytes,最大能表示的数的范围是40几亿,这边超过了
//一个有符号整型为4bytes,最大能表示的数的范围是20几亿
printf("result = %lld\n", result);
int n = str_octal_to_uint("0711");
printf("n=%d\n", n);
if (getuid() != 0)
{
fprintf(stderr, "miniftpd: must be started as root\n");
exit(EXIT_FAILURE);
}
session_t sess =
{
/* 控制连接 */
-1, "", "", "",
/* 父子进程通道 */
-1, -1
};
int listenfd = tcp_server(NULL, 5188);
int conn;
pid_t pid;
while (1)
{
conn = accept_timeout(listenfd, NULL, 0);
if (conn == -1)
ERR_EXIT("accept_tinmeout");
pid = fork();
if (pid == -1)
ERR_EXIT("fork");
if (pid == 0)
{
close(listenfd);
sess.ctrl_fd = conn;
begin_session(&sess);//与客户端的通信过程抽象为一个会话
}
else
close(conn);
}
return 0;
}
下面的文件都没动
privparent.h
#ifndef _PRIV_PARENT_H_
#define _PRIV_PARENT_H_
#include "session.h"
void handle_parent(session_t *sess);
#endif /* _PRIV_PARENT_H_ */
privparent.c
#include "privparent.h"
void handle_parent(session_t *sess)
{
char cmd;
while (1)
{
read(sess->parent_fd, &cmd, 1);
// 解析内部命令
// 处理内部命令
}
}
session.h
#ifndef _SESSION_H_
#define _SESSION_H_
#include "common.h"
typedef struct session
{
// 控制连接
int ctrl_fd;
char cmdline[MAX_COMMAND_LINE];
char cmd[MAX_COMMAND];
char arg[MAX_ARG];
// 父子进程通道
int parent_fd;
int child_fd;
} session_t;
void begin_session(session_t *sess);
#endif /* _SESSION_H_ */
session.c
#include "common.h"
#include "session.h"
#include "ftpproto.h"
#include "privparent.h"
void begin_session(session_t *sess)
{
struct passwd *pw = getpwnam("nobody");
if (pw == NULL)
return;
if (setegid(pw->pw_gid) < 0)
ERR_EXIT("setegid");
if (seteuid(pw->pw_uid) < 0)
ERR_EXIT("seteuid");
int sockfds[2];
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds) < 0)
ERR_EXIT("socketpair");
pid_t pid;
pid = fork();
if (pid < 0)
ERR_EXIT("fork");
if (pid == 0)
{
// ftp服务进程:处理控制连接和数据连接
close(sockfds[0]);
sess->child_fd = sockfds[1];
handle_child(sess);
}
else
{
// nobody进程:协助ftp服务进程与ftp客户端进行连接,why?
//因为当一个study用户登录成功后,会将ftp服务进程的用户名改为study的用户:将uid和gid改为study用户的uid和gid
//来降低ftp服务进程的权限,eg:PORT模式,它是服务器端连接客户端,服务器端要bind一个二元的端口号,二元端口
//不能用普通用户来bind,即ftp服务进程没有权限来bind端口,此时就需要nobody来协助bind二元端口与ftp客户端建立连接
//nobody比ftp服务进程的权限来的高
close(sockfds[1]);
sess->parent_fd = sockfds[0];
handle_parent(sess);
}
}
sysutil.h
#ifndef _SYS_UTIL_H_
#define _SYS_UTIL_H_
#include "common.h"
int tcp_server(const char *host, unsigned short port);
int getlocalip(char *ip);
void activate_nonblock(int fd);
void deactivate_nonblock(int fd);
int read_timeout(int fd, unsigned int wait_seconds);
int write_timeout(int fd, unsigned int wait_seconds);
int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds);
int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds);
ssize_t readn(int fd, void *buf, size_t count);
ssize_t writen(int fd, const void *buf, size_t count);
ssize_t recv_peek(int sockfd, void *buf, size_t len);
ssize_t readline(int sockfd, void *buf, size_t maxline);
void send_fd(int sock_fd, int fd);
int recv_fd(const int sock_fd);
#endif /* _SYS_UTIL_H_ */
sysutil.c
#include "sysutil.h"
/**
* tcp_server - 启动tcp服务器
* @host: 服务器IP地址或者服务器主机名
* @port: 服务器端口
* 成功返回监听套接字
*/
int tcp_server(const char *host, unsigned short port)
{
int listenfd;
if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
ERR_EXIT("tcp_server");
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
if (host != NULL)
{
if (inet_aton(host, &servaddr.sin_addr) == 0)
{
struct hostent *hp;
hp = gethostbyname(host);
if (hp == NULL)
ERR_EXIT("gethostbyname");
servaddr.sin_addr = *(struct in_addr*)hp->h_addr;
}
}
else
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
int on = 1;
if ((setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on))) < 0)
ERR_EXIT("gethostbyname");
if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("bind");
if (listen(listenfd, SOMAXCONN) < 0)
ERR_EXIT("listen");
return listenfd;
}
int getlocalip(char *ip)
{
char host[100] = {0};
if (gethostname(host, sizeof(host)) < 0)
return -1;
struct hostent *hp;
if ((hp = gethostbyname(host)) == NULL)
return -1;
strcpy(ip, inet_ntoa(*(struct in_addr*)hp->h_addr));
return 0;
}
/**
* activate_noblock - 设置I/O为非阻塞模式
* @fd: 文件描符符
*/
void activate_nonblock(int fd)
{
int ret;
int flags = fcntl(fd, F_GETFL);
if (flags == -1)
ERR_EXIT("fcntl");
flags |= O_NONBLOCK;
ret = fcntl(fd, F_SETFL, flags);
if (ret == -1)
ERR_EXIT("fcntl");
}
/**
* deactivate_nonblock - 设置I/O为阻塞模式
* @fd: 文件描符符
*/
void deactivate_nonblock(int fd)
{
int ret;
int flags = fcntl(fd, F_GETFL);
if (flags == -1)
ERR_EXIT("fcntl");
flags &= ~O_NONBLOCK;
ret = fcntl(fd, F_SETFL, flags);
if (ret == -1)
ERR_EXIT("fcntl");
}
/**
* read_timeout - 读超时检测函数,不含读操作
* @fd: 文件描述符
* @wait_seconds: 等待超时秒数,如果为0表示不检测超时
* 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
*/
int read_timeout(int fd, unsigned int wait_seconds)
{
int ret = 0;
if (wait_seconds > 0)
{
fd_set read_fdset;
struct timeval timeout;
FD_ZERO(&read_fdset);
FD_SET(fd, &read_fdset);
timeout.tv_sec = wait_seconds;
timeout.tv_usec = 0;
do
{
ret = select(fd + 1, &read_fdset, NULL, NULL, &timeout);
} while (ret < 0 && errno == EINTR);
if (ret == 0)
{
ret = -1;
errno = ETIMEDOUT;
}
else if (ret == 1)
ret = 0;
}
return ret;
}
/**
* write_timeout - 读超时检测函数,不含写操作
* @fd: 文件描述符
* @wait_seconds: 等待超时秒数,如果为0表示不检测超时
* 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
*/
int write_timeout(int fd, unsigned int wait_seconds)
{
int ret = 0;
if (wait_seconds > 0)
{
fd_set write_fdset;
struct timeval timeout;
FD_ZERO(&write_fdset);
FD_SET(fd, &write_fdset);
timeout.tv_sec = wait_seconds;
timeout.tv_usec = 0;
do
{
ret = select(fd + 1, NULL, NULL, &write_fdset, &timeout);
} while (ret < 0 && errno == EINTR);
if (ret == 0)
{
ret = -1;
errno = ETIMEDOUT;
}
else if (ret == 1)
ret = 0;
}
return ret;
}
/**
* accept_timeout - 带超时的accept
* @fd: 套接字
* @addr: 输出参数,返回对方地址
* @wait_seconds: 等待超时秒数,如果为0表示正常模式
* 成功(未超时)返回已连接套接字,超时返回-1并且errno = ETIMEDOUT
*/
int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
int ret;
socklen_t addrlen = sizeof(struct sockaddr_in);
if (wait_seconds > 0)
{
fd_set accept_fdset;
struct timeval timeout;
FD_ZERO(&accept_fdset);
FD_SET(fd, &accept_fdset);
timeout.tv_sec = wait_seconds;
timeout.tv_usec = 0;
do
{
ret = select(fd + 1, &accept_fdset, NULL, NULL, &timeout);
} while (ret < 0 && errno == EINTR);
if (ret == -1)
return -1;
else if (ret == 0)
{
errno = ETIMEDOUT;
return -1;
}
}
if (addr != NULL)
ret = accept(fd, (struct sockaddr*)addr, &addrlen);
else
ret = accept(fd, NULL, NULL);
/* if (ret == -1)
ERR_EXIT("accept");
*/
return ret;
}
/**
* connect_timeout - connect
* @fd: 套接字
* @addr: 要连接的对方地址
* @wait_seconds: 等待超时秒数,如果为0表示正常模式
* 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
*/
int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
int ret;
socklen_t addrlen = sizeof(struct sockaddr_in);
if (wait_seconds > 0)
activate_nonblock(fd);
ret = connect(fd, (struct sockaddr*)addr, addrlen);
if (ret < 0 && errno == EINPROGRESS)
{
printf("AAAAA\n");
fd_set connect_fdset;
struct timeval timeout;
FD_ZERO(&connect_fdset);
FD_SET(fd, &connect_fdset);
timeout.tv_sec = wait_seconds;
timeout.tv_usec = 0;
do
{
/* 一量连接建立,套接字就可写 */
ret = select(fd + 1, NULL, &connect_fdset, NULL, &timeout);
} while (ret < 0 && errno == EINTR);
if (ret == 0)
{
ret = -1;
errno = ETIMEDOUT;
}
else if (ret < 0)
return -1;
else if (ret == 1)
{
printf("BBBBB\n");
/* ret返回为1,可能有两种情况,一种是连接建立成功,一种是套接字产生错误,*/
/* 此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。 */
int err;
socklen_t socklen = sizeof(err);
int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen);
if (sockoptret == -1)
{
return -1;
}
if (err == 0)
{
printf("DDDDDDD\n");
ret = 0;
}
else
{
printf("CCCCCC\n");
errno = err;
ret = -1;
}
}
}
if (wait_seconds > 0)
{
deactivate_nonblock(fd);
}
return ret;
}
/**
* readn - 读取固定字节数
* @fd: 文件描述符
* @buf: 接收缓冲区
* @count: 要读取的字节数
* 成功返回count,失败返回-1,读到EOF返回<count
*/
ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf;
while (nleft > 0)
{
if ((nread = read(fd, bufp, nleft)) < 0)
{
if (errno == EINTR)
continue;
return -1;
}
else if (nread == 0)
return count - nleft;
bufp += nread;
nleft -= nread;
}
return count;
}
/**
* writen - 发送固定字节数
* @fd: 文件描述符
* @buf: 发送缓冲区
* @count: 要读取的字节数
* 成功返回count,失败返回-1
*/
ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf;
while (nleft > 0)
{
if ((nwritten = write(fd, bufp, nleft)) < 0)
{
if (errno == EINTR)
continue;
return -1;
}
else if (nwritten == 0)
continue;
bufp += nwritten;
nleft -= nwritten;
}
return count;
}
/**
* recv_peek - 仅仅查看套接字缓冲区数据,但不移除数据
* @sockfd: 套接字
* @buf: 接收缓冲区
* @len: 长度
* 成功返回>=0,失败返回-1
*/
ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
while (1)
{
int ret = recv(sockfd, buf, len, MSG_PEEK);
if (ret == -1 && errno == EINTR)
continue;
return ret;
}
}
/**
* readline - 按行读取数据
* @sockfd: 套接字
* @buf: 接收缓冲区
* @maxline: 每行最大长度
* 成功返回>=0,失败返回-1
*/
ssize_t readline(int sockfd, void *buf, size_t maxline)
{
int ret;
int nread;
char *bufp = buf;
int nleft = maxline;
while (1)
{
ret = recv_peek(sockfd, bufp, nleft);
if (ret < 0)
return ret;
else if (ret == 0)
return ret;
nread = ret;
int i;
for (i=0; i<nread; i++)
{
if (bufp[i] == '\n')
{
ret = readn(sockfd, bufp, i+1);
if (ret != i+1)
exit(EXIT_FAILURE);
return ret;
}
}
if (nread > nleft)
exit(EXIT_FAILURE);
nleft -= nread;
ret = readn(sockfd, bufp, nread);
if (ret != nread)
exit(EXIT_FAILURE);
bufp += nread;
}
return -1;
}
void send_fd(int sock_fd, int fd)
{
int ret;
struct msghdr msg;
struct cmsghdr *p_cmsg;
struct iovec vec;
char cmsgbuf[CMSG_SPACE(sizeof(fd))];
int *p_fds;
char sendchar = 0;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
p_cmsg = CMSG_FIRSTHDR(&msg);
p_cmsg->cmsg_level = SOL_SOCKET;
p_cmsg->cmsg_type = SCM_RIGHTS;
p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
p_fds = (int*)CMSG_DATA(p_cmsg);
*p_fds = fd;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &vec;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
vec.iov_base = &sendchar;
vec.iov_len = sizeof(sendchar);
ret = sendmsg(sock_fd, &msg, 0);
if (ret != 1)
ERR_EXIT("sendmsg");
}
int recv_fd(const int sock_fd)
{
int ret;
struct msghdr msg;
char recvchar;
struct iovec vec;
int recv_fd;
char cmsgbuf[CMSG_SPACE(sizeof(recv_fd))];
struct cmsghdr *p_cmsg;
int *p_fd;
vec.iov_base = &recvchar;
vec.iov_len = sizeof(recvchar);
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &vec;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
msg.msg_flags = 0;
p_fd = (int*)CMSG_DATA(CMSG_FIRSTHDR(&msg));
*p_fd = -1;
ret = recvmsg(sock_fd, &msg, 0);
if (ret != 1)
ERR_EXIT("recvmsg");
p_cmsg = CMSG_FIRSTHDR(&msg);
if (p_cmsg == NULL)
ERR_EXIT("no passed fd");
p_fd = (int*)CMSG_DATA(p_cmsg);
recv_fd = *p_fd;
if (recv_fd == -1)
ERR_EXIT("no passed fd");
return recv_fd;
}
Makefile
.PHONY:clean
CC=gcc
CFLAGS=-Wall -g
BIN=miniftpd
OBJS=main.o sysutil.o session.o ftpproto.o privparent.o str.o
$(BIN):$(OBJS)
$(CC) $(CFLAGS) $^ -o $@
%.o:%.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f *.o $(BIN)
- 测试1:
去除\r\n后的结果如下:是第二个
printf("cmdline=[%s]\n", sess->cmdline);
// 去除\r\n
str_trim_crlf(sess->cmdline);
printf("cmdline=[%s]\n", sess->cmdline);
重启服务端,重新连接客户端
- 测试2:
// 解析FTP命令与参数
str_split(sess->cmdline, sess->cmd, sess->arg, ' ');
printf("cmd=[%s] arg=[%s]\n", sess->cmd, sess->arg);
- 测试3:
char *str1 = " a b";
char *str2 = " ";
if (str_all_space(str1))
printf("str1 all space\n");
else
printf("str1 not all space\n");
if (str_all_space(str2))
printf("str2 all space\n");
else
printf("str2 not all space\n");
- 测试4:
//char *str3 = "abcDef"; // 指针指向一个字符串常量,常量不能被修改,调用str_upper会出错
char str3[] = "abcDef"; //定义了一个字符数组,将常量存入字符数组变量中,str3本质上是变量,只不过将常量内容搬到变量中而已
str_upper(str3);
printf("str3=%s\n", str3);
- 测试5:
long long result = str_to_longlong("12345678901234");//一个无符号整型为4bytes,最大能表示的数的范围是40几亿,这边超过了
//一个有符号整型为4bytes,最大能表示的数的范围是20几亿
printf("result = %lld\n", result);
- 测试6:
int n = str_octal_to_uint("0711");
printf("n=%d\n", n);