#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include "get_num.h"
/* Print a diagnostic message that contains a function name ('fname'),
the value of a command-line argument ('arg'), the name of that
command-line argument ('name'), and a diagnostic error message ('msg'). */
static void
gnFail(const char *fname, const char *msg, const char *arg, const char *name)
{
fprintf(stderr, "%s error", fname);
if (name != NULL)
fprintf(stderr, " (in %s)", name);
fprintf(stderr, ": %s\n", msg);
if (arg != NULL && *arg != '\0')
fprintf(stderr, " offending text: %s\n", arg);
exit(EXIT_FAILURE);
}
/* Convert a numeric command-line argument ('arg') into a long integer,
returned as the function result. 'flags' is a bit mask of flags controlling
how the conversion is done and what diagnostic checks are performed on the
numeric result; see get_num.h for details.
'fname' is the name of our caller, and 'name' is the name associated with
the command-line argument 'arg'. 'fname' and 'name' are used to print a
diagnostic message in case an error is detected when processing 'arg'. */
static long
getNum(const char *fname, const char *arg, int flags, const char *name)
{
long res;
char *endptr;
int base;
if (arg == NULL || *arg == '\0')
gnFail(fname, "null or empty string", arg, name);
base = (flags & GN_ANY_BASE) ? 0 : (flags & GN_BASE_8) ? 8 :
(flags & GN_BASE_16) ? 16 : 10;
errno = 0;
res = strtol(arg, &endptr, base);
if (errno != 0)
gnFail(fname, "strtol() failed", arg, name);
if (*endptr != '\0')
gnFail(fname, "nonnumeric characters", arg, name);
if ((flags & GN_NONNEG) && res < 0)
gnFail(fname, "negative value not allowed", arg, name);
if ((flags & GN_GT_0) && res <= 0)
gnFail(fname, "value must be > 0", arg, name);
return res;
}
/* Convert a numeric command-line argument string to a long integer. See the
comments for getNum() for a description of the arguments to this function. */
long
getLong(const char *arg, int flags, const char *name)
{
return getNum("getLong", arg, flags, name);
}
/* Convert a numeric command-line argument string to an integer. See the
comments for getNum() for a description of the arguments to this function. */
int
getInt(const char *arg, int flags, const char *name)
{
long res;
res = getNum("getInt", arg, flags, name);
if (res > INT_MAX || res < INT_MIN)
gnFail("getInt", "integer out of range", arg, name);
return (int) res;
}
#define _BSD_SOURCE
#include "tlpi_hdr.h"
#define MAX_ALLOCS 1000000
int
main(int argc, char *argv[])
{
char *ptr[MAX_ALLOCS];
int freeStep, freeMin, freeMax, blockSize, numAllocs, j;
printf("\n");
if (argc < 3 || strcmp(argv[1], "--help") == 0)
usageErr("%s num-allocs block-size [step [min [max]]]\n", argv[0]);
numAllocs = getInt(argv[1], GN_GT_0, "num-allocs");
if (numAllocs > MAX_ALLOCS)
cmdLineErr("num-allocs > %d\n", MAX_ALLOCS);
blockSize = getInt(argv[2], GN_GT_0 | GN_ANY_BASE, "block-size");
freeStep = (argc > 3) ? getInt(argv[3], GN_GT_0, "step") : 1;
freeMin = (argc > 4) ? getInt(argv[4], GN_GT_0, "min") : 1;
freeMax = (argc > 5) ? getInt(argv[5], GN_GT_0, "max") : numAllocs;
if (freeMax > numAllocs)
cmdLineErr("free-max > num-allocs\n");
printf("Initial program break: %10p\n", sbrk(0));
printf("Allocating %d*%d bytes\n", numAllocs, blockSize);
for (j = 0; j < numAllocs; j++) {
ptr[j] = malloc(blockSize);
if (ptr[j] == NULL)
errExit("malloc");
}
printf("Program break is now: %10p\n", sbrk(0));
printf("Freeing blocks from %d to %d in steps of %d\n",
freeMin, freeMax, freeStep);
for (j = freeMin - 1; j < freeMax; j += freeStep)
free(ptr[j]);
printf("After free(), program break is: %10p\n", sbrk(0));
exit(EXIT_SUCCESS);
}
#include <stdarg.h>
#include "error_functions.h"
#include "tlpi_hdr.h"
#include "ename.c.inc" /* Defines ename and MAX_ENAME */
#ifdef __GNUC__ /* Prevent 'gcc -Wall' complaining */
__attribute__ ((__noreturn__)) /* if we call this function as last */
#endif /* statement in a non-void function */
static void
terminate(Boolean useExit3)
{
char *s;
/* Dump core if EF_DUMPCORE environment variable is defined and
is a nonempty string; otherwise call exit(3) or _exit(2),
depending on the value of 'useExit3'. */
s = getenv("EF_DUMPCORE");
if (s != NULL && *s != '\0')
abort();
else if (useExit3)
exit(EXIT_FAILURE);
else
_exit(EXIT_FAILURE);
}
/* Diagnose 'errno' error by:
* outputting a string containing the error name (if available
in 'ename' array) corresponding to the value in 'err', along
with the corresponding error message from strerror(), and
* outputting the caller-supplied error message specified in
'format' and 'ap'. */
static void
outputError(Boolean useErr, int err, Boolean flushStdout,
const char *format, va_list ap)
{
#define BUF_SIZE 500
char buf[BUF_SIZE], userMsg[BUF_SIZE], errText[BUF_SIZE];
vsnprintf(userMsg, BUF_SIZE, format, ap);
if (useErr)
snprintf(errText, BUF_SIZE, " [%s %s]",
(err > 0 && err <= MAX_ENAME) ?
ename[err] : "?UNKNOWN?", strerror(err));
else
snprintf(errText, BUF_SIZE, ":");
snprintf(buf, BUF_SIZE, "ERROR%s %s\n", errText, userMsg);
if (flushStdout)
fflush(stdout); /* Flush any pending stdout */
fputs(buf, stderr);
fflush(stderr); /* In case stderr is not line-buffered */
}
/* Display error message including 'errno' diagnostic, and
return to caller */
void
errMsg(const char *format, ...)
{
va_list argList;
int savedErrno;
savedErrno = errno; /* In case we change it here */
va_start(argList, format);
outputError(TRUE, errno, TRUE, format, argList);
va_end(argList);
errno = savedErrno;
}
/* Display error message including 'errno' diagnostic, and
terminate the process */
void
errExit(const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errno, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
/* Display error message including 'errno' diagnostic, and
terminate the process by calling _exit().
The relationship between this function and errExit() is analogous
to that between _exit(2) and exit(3): unlike errExit(), this
function does not flush stdout and calls _exit(2) to terminate the
process (rather than exit(3), which would cause exit handlers to be
invoked).
These differences make this function especially useful in a library
function that creates a child process that must then terminate
because of an error: the child must terminate without flushing
stdio buffers that were partially filled by the caller and without
invoking exit handlers that were established by the caller. */
void
err_exit(const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errno, FALSE, format, argList);
va_end(argList);
terminate(FALSE);
}
/* The following function does the same as errExit(), but expects
the error number in 'errnum' */
void
errExitEN(int errnum, const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errnum, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
/* Print an error message (without an 'errno' diagnostic) */
void
fatal(const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(FALSE, 0, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
/* Print a command usage error message and terminate the process */
void
usageErr(const char *format, ...)
{
va_list argList;
fflush(stdout); /* Flush any pending stdout */
fprintf(stderr, "Usage: ");
va_start(argList, format);
vfprintf(stderr, format, argList);
va_end(argList);
fflush(stderr); /* In case stderr is not line-buffered */
exit(EXIT_FAILURE);
}
/* Diagnose an error in command-line arguments and
terminate the process */
void
cmdLineErr(const char *format, ...)
{
va_list argList;
fflush(stdout); /* Flush any pending stdout */
fprintf(stderr, "Command-line usage error: ");
va_start(argList, format);
vfprintf(stderr, format, argList);
va_end(argList);
fflush(stderr); /* In case stderr is not line-buffered */
exit(EXIT_FAILURE);
}
brk/sbrk的使用 : from https://www.cnblogs.com/shayu/p/3371880.html
linux中几种管理内存的方式有:STL,new/delete,malloc/free,brk/sbrk,mmap/unmap
brk/sbrk:提供底层的内存分配函数
功能是共同维护系统的一个指针,主要用于对同类型的大块数据的动态存放,定义如下:
int brk(void *end_data_segment); // 通过移动指针分配空间,释放空间 void* sbrk(ptrdiff_t increment); // 分配内存空间,返回指定大小空间的地址
sbrk用法:第一次调用时,系统分配一大块空闲地址,把首地址返回,分配给一个指针phead,作为首地址不动;下一次调用时,返回当前位置的地址,分配给一个指针pnow,并把指针指向+increment的地方;
brk用法:对参数中pnow做绝对位置调整,调动指针左右移动,左移-释放空间,右移-分配空间
如果内存分配失败,二者都返回-1
注意:
1、每个进程可访问的虚拟内存空间为3G,在程序编译时,系统只分配并不大的数据段空间,称为1页,共4096字节,如果这块空间不够,就使用sbrk函数将数据段的下界移动,获得新的空间,这也是malloc的原理。
2、phead和pnow之间的内存空间才是合法内存,在没有分配空间的地方存取数据会发生段错误,在合法内存之外存取数据会发生越界,导致不可预知的错误。
例:将1-10000之间的所有素数存放到缓冲,并输出
#include <stdio.h>
#include <unistd.h> // 使用brk/sbrk,需要使用这个头文件
int main()
{
//存放数据
int i = 2;
int* phead; // 指向首位置
int* pnow; // 指向当前指针位置
pnow = sbrk(0); // 先分配空闲区域
phead = pnow; // 固定首位置不变
for(i=2; i<10000; i++)
{
if(isPrime(i)) // 该函数用于判断是否是素数
{
brk(pnow+1);// 分配4个字节
*pnow = i; // 存入数据
pnow = sbrk(0); // 指向系统指针上次移动到的那个位置
}
}
//打印数据
pnow = phead; // 从头打印
while(pnow!=sbrk(0))
{
printf("%d\n",*pnow);
pnow++;
}
brk(phead); // 释放空间
}