Images in this article are not well-scaled and some of them may not be accessed.

Disclaimer: This passage contains notes and insights gathered from the internet. The content is used for educational purposes and attributed to the original authors. I do not claim any ownership over the material.

System Arguments

Screenshot 2023-01-10 at 12.24.33.png

Memory Structure

image.pngimage.png
stack的memory地址是最大的

Pointer

Syntax

/* one pointer, one regular int */
int *pointer1, nonpointer1;

/* two pointers */
int *pointer1, *pointer2;

if you declare multiple pointers on the same line, you must precede each of them with an asterisk.

Graphs

image.png
image.png
image.png

Dynamic Memory Allocation

image.png

malloc & free

// Determine the size of an integer and allocate that much heap space.
malloc(sizeof(int));

The malloc function returns a void * type, which represents a generic pointer to a non-specified type (or to any type).

p = (int *) malloc(sizeof(int));
free(p);
p = NULL;

Syntax

typedef struct {
char * name;
int age;
} person;

person * myperson = (person *) malloc(sizeof(person));
  • The reason we write person * before the call to malloc() is that malloc() returns a void pointer, which is a pointer that doesn’t have a type. Writing person * in front of it is called typecasting, and changes the type of the pointer returned from malloc() to be person.
  • Note that sizeof is not an actual function, because the compiler interprets it and translates it to the actual memory size of the person struct.
// This step will evaluate the size of whatever we would get back from dereferencing ptr
int *ptr = malloc(sizeof(*ptr));

float *ptr;
/* hundreds of lines of code */
ptr = malloc( sizeof(*ptr) );

When 0 is assigned to a pointer, the pointer becomes a null pointer, in other words, it points to nothing.

free(ptr);
ptr = NULL;

Allocated Arrays & Strings

int *arr;
char *c_arr;

// allocate an array of 20 ints on the heap:
arr = malloc(sizeof(int) * 20);

// allocate an array of 10 chars on the heap:
c_arr = malloc(sizeof(char) * 10);

image.png

eg

/* these two statements are identical: both put 8 in index 0 */
d_array[0] = 8; // put 8 in index 0 of the d_array
*d_array = 8; // in the location pointed to by d_array store 8

Array

Normal Array

int a[5];
printf("a: %x, &a: %x, &a[0]: %x", a, &a, &a[0]);
// a: ffffefb0 &a: ffffefb0 &a[0]: ffffefb0
// a+1: ffffefb4 &a+1: ffffefc4
// a[1]: ffffefb4 &a[1]: ffffefb4 (&a)[1]: Array given(ffffefc4)

Two Dimensional Array

Static Array

image.png

Dynamic Array

  • Make a single call to malloc, allocating one large chunk of heap space to store all NxM array elements.
    • Memory Efficient. All elements will be allocated at once, in contiguous memory locations
#define N 3
#define M 4

int main() {
int *two_d_array; // the type is a pointer to an int (the element type)

// allocate in a single malloc of N x M int-sized elements:
two_d_array = malloc(sizeof(int) * N * M);

if (two_d_array == NULL) {
printf("ERROR: malloc failed!\n");
exit(1);
}

...

image.png

void init2D(int *arr, int rows, int cols) {
int i, j;
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
arr[i*cols + j] = 0;
}
}
}
  • Make multiple calls to malloc, allocating an array of arrays. First, allocate a 1D array of N pointers to the element type, with a 1D array of pointers for each row in the 2D array. Then, allocate N 1D arrays of size M to store the set of column values for each row in the 2D array. Assign the addresses of each of these N arrays to the elements of the first array of N pointers.
    • More programmer-friendly
// the 2D array variable is declared to be `int **` (a pointer to an int *)
// a dynamically allocated array of dynamically allocated int arrays
// (a pointer to pointers to ints)
int **two_d_array;
int i;

// allocate an array of N pointers to ints
// malloc returns the address of this array (a pointer to (int *)'s)
two_d_array = malloc(sizeof(int *) * N);

// for each row, malloc space for its column elements and add it to
// the array of arrays
for (i = 0; i < N; i++) {
// malloc space for row i's M column elements
two_d_array[i] = malloc(sizeof(int) * M);
}

image.png

void init2D_Method2(int **arr, int rows, int cols) {
int i,j;

for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
arr[i][j] = 0;
}
}
}

Strings and String Lib

// strcpy copies the bytes from the source parameter (str1) to the
// destination parameter (str2) and null terminates the copy.
strcpy(str2, str1);

Dynamically Allocated Strings

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
int size;
char str[64]; // statically allocated
char *new_str = NULL; // for dynamically allocated

strcpy(str, "Hello");
size = strlen(str); // returns 5

new_str = malloc(sizeof(char) * (size+1)); // need space for '\0'
if(new_str == NULL) {
printf("Error: malloc failed! exiting.\n");
exit(1);
}
strcpy(new_str, str);
printf("%s %s\n", str, new_str); // prints "Hello Hello"

strcat(str, " There"); // concatenate " There" to the end of str
printf("%s\n", str); // prints "Hello There"

free(new_str); // free malloc'ed space when done
new_str = NULL;

return 0;
}

The string library (string.h) is particularly useful when writing programs that use C strings. The stdlib.h and stdio.h libraries also contain functions for string manipulation, and the ctype.h library contains functions for manipulating individual character values.

strlen & strcpy & strncpy

// returns the number of characters in the string (not including the null character)
int strlen(char *s);

// copies string src to string dst up until the first '\0' character in src
// (the caller needs to make sure src is initialized correctly and
// dst has enough space to store a copy of the src string)
// returns the address of the dst string
char *strcpy(char *dst, char *src);

// like strcpy but copies up to the first '\0' or size characters
// (this provides some safety to not copy beyond the bounds of the dst
// array if the src string is not well formed or is longer than the
// space available in the dst array); size_t is an unsigned integer type
char *strncpy(char *dst, char *src, size_t size);
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // include the string library

int main() {
// variable declarations that will be used in examples
int len, i, ret;
char str[32];
char *d_str, *ptr;

strcpy(str, "Hello There");
len = strlen(str); // len is 11

d_str = malloc(sizeof(char) * (len+1));
if (d_str == NULL) {
printf("Error: malloc failed\n");
exit(1);
}

strncpy(d_str, str, 5);
d_str[5] = '\0'; // explicitly add null terminating character to end

printf("%d:%s\n", strlen(str), str); // prints 11:Hello There
printf("%d:%s\n", strlen(d_str), d_str); // prints 5:Hello

free(d_str);

return 0;
}

strcmp & strncmp

int strcmp(char *s1, char *s2);
// returns 0 if s1 and s2 are the same strings
// a value < 0 if s1 is less than s2
// a value > 0 if s1 is greater than s2

int strncmp(char *s1, char *s2, size_t n);
// compare s1 and s2 up to at most n characters
strcpy(str, "alligator");
strcpy(d_str, "Zebra");

ret = strcmp(str,d_str);
if (ret == 0) {
printf("%s is equal to %s\n", str, d_str);
} else if (ret < 0) {
printf("%s is less than %s\n", str, d_str);
} else {
printf("%s is greater than %s\n", str, d_str); // true for these strings
}

ret = strncmp(str, "all", 3); // returns 0: they are equal up to first 3 chars

strcat & strstr & strchr

// append chars from src to end of dst
// returns ptr to dst and adds '\0' to end
char *strcat(char *dst, char *src)

// append the first chars from src to end of dst, up to a maximum of size
// returns ptr to dst and adds '\0' to end
char *strncat(char *dst, char *src, size_t size);
// locate a substring inside a string
// (const means that the function doesn't modify string)
// returns a pointer to the beginning of substr in string
// returns NULL if substr not in string
char *strstr(const char *string, char *substr);

// locate a character (c) in the passed string (s)
// (const means that the function doesn't modify s)
// returns a pointer to the first occurrence of the char c in string
// or NULL if c is not in the string
char *strchr(const char *s, int c);
char str[32];
char *ptr;

strcpy(str, "Zebra fish");
strcat(str, " stripes"); // str gets "Zebra fish stripes"
printf("%s\n", str); // prints: Zebra fish stripes

strncat(str, " are black.", 8);
printf("%s\n", str); // prints: Zebra fish stripes are bla (spaces count)

ptr = strstr(str, "trip");
if (ptr != NULL) {
printf("%s\n", ptr); // prints: tripes are bla
}

ptr = strchr(str, 'e');
if (ptr != NULL) {
printf("%s\n", ptr); // prints: ebra fish stripes are bla
}

strtok & strtok_r

A token refers to a subsequence of characters in a string separated by any number of delimiter characters of the programmer’s choosing.

char *strtok(char *str, const char *delim);

// a reentrant version of strtok (reentrant is defined in later chapters):
char *strtok_r(char *str, const char *delim, char **saveptr);
/*
* Extract whitespace-delimited tokens from a line of input
* and print them one per line.
*
* to compile:
* gcc -g -Wall strtokexample.c
*
* example run:
* Enter a line of text: aaaaa bbbbbbbbb cccccc
*
* The input line is:
* aaaaa bbbbbbbbb cccccc
* Next token is aaaaa
* Next token is bbbbbbbbb
* Next token is cccccc
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main() {
/* whitespace stores the delim string passed to strtok. The delim
* string is initialized to the set of characters that delimit tokens
* We initialize the delim string to the following set of chars:
* ' ': space '\t': tab '\f': form feed '\r': carriage return
* '\v': vertical tab '\n': new line
* (run "man ascii" to list all ASCII characters)
*
* This line shows one way to statically initialize a string variable
* (using this method the string contents are constant, meaning that they
* cannot be modified, which is fine for the way we are using the
* whitespace string in this program).
*/
char *whitespace = " \t\f\r\v\n"; /* Note the space char at beginning */

char *token; /* The next token in the line. */
char *line; /* The line of text read in that we will tokenize. */

/* Allocate some space for the user's string on the heap. */
line = malloc(200 * sizeof(char));
if (line == NULL) {
printf("Error: malloc failed\n");
exit(1);
}

/* Read in a line entered by the user from "standard in". */
printf("Enter a line of text:\n");
line = fgets(line, 200 * sizeof(char), stdin);
if (line == NULL) {
printf("Error: reading input failed, exiting...\n");
exit(1);
}
printf("The input line is:\n%s\n", line);

/* Divide the string into tokens. */
token = strtok(line, whitespace); /* get the first token */
while (token != NULL) {
printf("Next token is %s\n", token);
token = strtok(NULL, whitespace); /* get the next token */
}

free(line);

return 0;
}

直接使用token = strtok(NULL, whitespace)会报错。只有当第一次正确切割,才能使用此语句进行下一次切割。

sprintf

// like printf(), the format string allows for placeholders like %d, %f, etc.
// pass parameters after the format string to fill them in
int sprintf(char *s, const char *format, ...);
char str[64];
float ave = 76.8;
int num = 2;

// initialize str to format string, filling in each placeholder with
// a char representation of its arguments' values
sprintf(str, "%s is %d years old and in grade %d", "Henry", 12, 7);
printf("%s\n", str); // prints: Henry is 12 years old and in grade 7

sprintf(str, "The average grade on exam %d is %g", num, ave);
printf("%s\n", str); // prints: The average grade on exam 2 is 76.8

stdlib.h & ctype.h

#include <stdlib.h>   // include stdlib and ctypes to use these
#include <ctype.h>

int islower(ch);
int isupper(ch); // these functions return a non-zero value if the
int isalpha(ch); // test is TRUE, otherwise they return 0 (FALSE)
int isdigit(ch);
int isalnum(ch);
int ispunct(ch);
int isspace(ch);
char tolower(ch); // returns ASCII value of lower-case of argument
char toupper(ch);
char str[64];
int len, i;

strcpy(str, "I see 20 ZEBRAS, GOATS, and COWS");

if ( islower(str[2]) ){
printf("%c is lower case\n", str[2]); // prints: s is lower case
}

len = strlen(str);
for (i = 0; i < len; i++) {
if ( isupper(str[i]) ) {
str[i] = tolower(str[i]);
} else if( isdigit(str[i]) ) {
str[i] = 'X';
}
}
printf("%s\n", str); // prints: i see XX zebras, goats, and cows
#include <stdlib.h>

int atoi(const char *nptr); // convert a string to an integer
double atof(const char *nptr); // convert a string to a float

Struct

#include <stdio.h>
#include <string.h>

/* define a new struct type (outside function bodies) */
struct studentT {
char name[64];
int age;
float gpa;
int grad_yr;
};

/* function prototypes */
int checkID(struct studentT s1, int min_age);
void changeName(char *old, char *new);

int main() {
int can_vote;
// declare variables of struct type:
struct studentT student1, student2;

// access field values using .
strcpy(student1.name, "Ruth");
student1.age = 17;
student1.gpa = 3.5;
student1.grad_yr = 2021;

// structs are lvalues
student2 = student1;
strcpy(student2.name, "Frances");
student2.age = student1.age + 4;

// passing a struct
can_vote = checkID(student1, 18);
printf("%s %d\n", student1.name, can_vote);

can_vote = checkID(student2, 18);
printf("%s %d\n", student2.name, can_vote);

// passing a struct field value
changeName(student2.name, "Kwame");
printf("student 2's name is now %s\n", student2.name);

return 0;
}

int checkID(struct studentT s, int min_age) {
int ret = 1;

if (s.age < min_age) {
ret = 0;
// changes age field IN PARAMETER COPY ONLY
s.age = min_age + 1;
}
return ret;
}

void changeName(char *old, char *new) {
if ((old == NULL) || (new == NULL)) {
return;
}
strcpy(old,new);
}

pointer & malloc

struct studentT s;
struct studentT *sptr;

// think very carefully about the type of each field when
// accessing it (name is an array of char, age is an int ...)
strcpy(s.name, "Freya");
s.age = 18;
s.gpa = 4.0;
s.grad_yr = 2020;

// malloc space for a struct studentT for sptr to point to:
sptr = malloc(sizeof(struct studentT));
if (sptr == NULL) {
printf("Error: malloc failed\n");
exit(1);
}
// the grad_yr field of what sptr points to gets 2021:
(*sptr).grad_yr = 2021;

// the age field of what sptr points to gets s.age plus 1:
(*sptr).age = s.age + 1;

// the gpa field of what sptr points to gets 3.5:
sptr->gpa = 3.5;

// the name field of what sptr points to is a char *
// (can use strcpy to init its value):
strcpy(sptr->name, "Lars");

image.png

Pointer fields in Structs

struct personT {
char *name; // for a dynamically allocated string field
int age;
};

int main() {
struct personT p1, *p2;

// need to malloc space for the name field:
p1.name = malloc(sizeof(char) * 8);
strcpy(p1.name, "Zhichen");
p1.age = 22;


// first malloc space for the struct:
p2 = malloc(sizeof(struct personT));

// then malloc space for the name field:
p2->name = malloc(sizeof(char) * 4);
strcpy(p2->name, "Vic");
p2->age = 19;
...

// Note: for strings, we must allocate one extra byte to hold the
// terminating null character that marks the end of the string.
}

image.png

Array of Structs

struct studentT classroom1[40];   // an array of 40 struct studentT

struct studentT *classroom2; // a pointer to a struct studentT
// (for a dynamically allocated array)

struct studentT *classroom3[40]; // an array of 40 struct studentT *
// (each element stores a (struct studentT *)

Self-Referential Structs

A linked list is one way to implement a list abstract data type.

struct node {
int data; // used to store a list element's data value
struct node *next; // used to point to the next node in the list
};
struct node *head, *temp;
int i;

head = NULL; // an empty linked list

head = malloc(sizeof(struct node)); // allocate a node
if (head == NULL) {
printf("Error malloc\n");
exit(1);
}
head->data = 10; // set the data field
head->next = NULL; // set next to NULL (there is no next element)

// add 2 more nodes to the head of the list:
for (i = 0; i < 2; i++) {
temp = malloc(sizeof(struct node)); // allocate a node
if (temp == NULL) {
printf("Error malloc\n");
exit(1);
}
temp->data = i; // set data field
temp->next = head; // set next to point to current first node
head = temp; // change head to point to newly added node
}

image.png

I/O in C

Redirection

#  redirect a.out's stdin to read from file infile.txt:
$ ./a.out < infile.txt

# redirect a.out's stdout to print to file outfile.txt:
$ ./a.out > outfile.txt

# redirect a.out's stdout and stderr to a file out.txt
$ ./a.out &> outfile.txt

# redirect all three to different files:
# (< redirects stdin, 1> stdout, and 2> stderr):
$ ./a.out < infile.txt 1> outfile.txt 2> errorfile.txt

printf

%f, %g: placeholders for a float or double value
%d: placeholder for a decimal value (char, short, int)
%u: placeholder for an unsigned decimal
%c: placeholder for a single character
%s: placeholder for a string value
%p: placeholder to print an address value

%ld: placeholder for a long value
%lu: placeholder for an unsigned long value
%lld: placeholder for a long long value
%llu: placeholder for an unsigned long long value
%5.3f: print float value in space 5 chars wide, with 3 places beyond decimal
%20s: print the string value in a field of 20 chars wide, right justified
%-20s: print the string value in a field of 20 chars wide, left justified
%8d: print the int value in a field of 8 chars wide, right justified
%-8d: print the int value in a field of 8 chars wide, left justified
%x:     print value in hexadecimal (base 16)
%o: print value in octal (base 8)
%d: print value in signed decimal (base 10)
%u: print value in unsigned decimal (unsigned base 10)
%e: print float or double in scientific notation
(there is no formatting option to display a value in binary)

scanf

int x;
float pi;

// read in an int value followed by a float value ("%d%g")
// store the int value at the memory location of x (&x)
// store the float value at the memory location of pi (&pi)
scanf("%d%g", &x, &pi);

getchar & putchar

getchar is particularly useful in C programs that need to support careful error detection and handling of badly formed user input (scanf is not robust in this way).

ch = getchar();  // read in the next char value from stdin
putchar(ch); // write the value of ch to stdout

File Input/Output

// 1. Declare a FILE * variable
FILE *infile;
FILE *outfile;


// 2. Open the file: associate the variable with an actual file stream by calling fopen.
infile = fopen("input.txt", "r"); // relative path name of file, read mode
if (infile == NULL) {
printf("Error: unable to open file %s\n", "input.txt");
exit(1);
}
// fopen with absolute path name of file, write mode
outfile = fopen("/home/me/output.txt", "w");
if (outfile == NULL) {
printf("Error: unable to open outfile\n");
exit(1);
}


// 3. Use I/O operations to read, write, or move the current position in the file
int ch; // EOF is not a char value, but is an int.
// since all char values can be stored in int, use int for ch
ch = getc(infile); // read next char from the infile stream
if (ch != EOF) {
putc(ch, outfile); // write char value to the outfile stream
}


// 4. Close the file: use fclose to close the file when the program no longer needs it
fclose(infile);
fclose(outfile);

The stdio library also provides functions to change the current position in a file

// to reset current position to beginning of file
void rewind(FILE *f);

rewind(infile);

// to move to a specific location in the file:
fseek(FILE *f, long offset, int whence);

fseek(f, 0, SEEK_SET); // seek to the beginning of the file
fseek(f, 3, SEEK_CUR); // seek 3 chars forward from the current position
fseek(f, -3, SEEK_END); // seek 3 chars back from the end of the file

stdio.h

// ---------------
// Character Based
// ---------------

// returns the next character in the file stream (EOF is an int value)
int fgetc(FILE *f);

// writes the char value c to the file stream f
// returns the char value written
int fputc(int c, FILE *f);

// pushes the character c back onto the file stream
// at most one char (and not EOF) can be pushed back
int ungetc(int c, FILE *f);

// like fgetc and fputc but for stdin and stdout
int getchar();
int putchar(int c);
// -------------
// String Based
// -------------

// reads at most n-1 characters into the array s stopping if a newline is
// encountered, newline is included in the array which is '\0' terminated
char *fgets(char *s, int n, FILE *f);

// writes the string s (make sure '\0' terminated) to the file stream f
int fputs(char *s, FILE *f);
// ---------
// Formatted
// ---------

// writes the contents of the format string to file stream f
// (with placeholders filled in with subsequent argument values)
// returns the number of characters printed
int fprintf(FILE *f, char *format, ...);

// like fprintf but to stdout
int printf(char *format, ...);

// use fprintf to print stderr:
fprintf(stderr, "Error return value: %d\n", ret);

// read values specified in the format string from file stream f
// store the read-in values to program storage locations of types
// matching the format string
// returns number of input items converted and assigned
// or EOF on error or if EOF was reached
int fscanf(FILE *f, char *format, ...);

// like fscanf but reads from stdin
int scanf(char *format, ...);

fscanf Examples

int x;
double d;
char c, array[MAX];

// write int & char values to file separated by colon with newline at the end
fprintf(outfile, "%d:%c\n", x, c);

// read an int & char from file where int and char are separated by a comma
fscanf(infile, "%d,%c", &x, &c);

// read a string from a file into array (stops reading at whitespace char)
fscanf(infile,"%s", array);

// read a double and a string up to 24 chars from infile
fscanf(infile, "%lf %24s", &d, array);

// read in a string consisting of only char values in the specified set (0-5)
// stops reading when...
// 20 chars have been read OR
// a character not in the set is reached OR
// the file stream reaches end-of-file (EOF)
fscanf(infile, "%20[012345]", array);

// read in a string; stop when reaching a punctuation mark from the set
fscanf(infile, "%[^.,:!;]", array);

// read in two integer values: store first in long, second in int
// then read in a char value following the int value
fscanf(infile, "%ld %d%c", &x, &b, &c);

Command Line Arguments

Screenshot 2023-01-12 at 13.05.17.png

void * and Type Casting

The C type void * represents a generic pointer — a pointer to any type, or a pointer to an unspecified type.

void *gen_ptr;
int x;
char ch;

gen_ptr = &x; // gen_ptr can be assigned the address of an int
gen_ptr = &ch; // or the address of a char (or the address of any type)

Two Usages

  • A call to malloc recasts its void * return type to the specific pointer type of the variable used to store its returned heap memory address
int *array;
char *str;

array = (int *)malloc(sizeof(int) * 10); // recast void * return value
str = (char *)malloc(sizeof(char) * 20);

*array = 10;
str[0] = 'a';
  • Students often encounter the void * when creating threads. Using a void * parameter type in a thread function allows the thread to take any type of application-specific pointer.
/*
* an application-specific pthread main function
* must have this function prototype: int func_name(void *args)
*
* any given implementation knows what type is really passed in
* args: pointer to an int value
*/
int my_thr_main(void *args) {
int num;

// first recast args to an int *, then dereference to get int value
num = *((int *)args); // num gets 6
...
}

int main() {
int ret, x;
pthread_t tid;

x = 6;
// pass the address of int variable (x) to pthread_create's void * param
// (we recast &x as a (void *) to match the type of pthread_create's param)
ret = pthread_create(&tid, NULL,
my_thr_main, // a thread main function
(void *)(&x)); // &x will be passed to my_thr_main
// ...

C Libraries

A C library consists of two parts:

  • The application programming interface (API) to the library, which gets defined in one or more header files (.h files) that must be included in C source code files that plan to use the library.
  • The implementation of the library’s functionality, often made available to programs in a precompiled binary format that gets linked (added) into the binary executable created by gcc. Precompiled library code might be in an archive file (libsomelib.a) containing several .o files that can be statically linked into the executable file at compile time. Alternatively, it may consist of a shared object file (libsomelib.so) that can be dynamically linked at runtime into a running program.

Large C programs tend to use many C libraries, some of which gcc links implicitly, whereas others require explicit linking with the -l command line option to gcc.
$ gcc -o myprog myprog.c -lpthread -lreadline

It also allows the compiler to choose to dynamically link when both a shared object (.so) and an archive (.a) version of a library are available.

If users want to statically link libraries, then they can explicitly specify static linking in the gcc command line. The --static option provides one method for requesting static linking
$ gcc -o myprog myprog.c --static -lpthread -lreadline

struct & union

struct

Usage:

  • Serialization of data
  • Passing multiple arguments in and out of functions through a single argument
  • Data structures such as linked lists, binary trees, and more
// Definition
struct Tag {
Members
};

// Create a single structure
struct Tag name_of_single_structure;

struct Person {
...
} person1, person2, p[20];

// access a variable of the structure
name_of_single_structure.name_of_variable;

// it can be a return type of a function
struct database fn();

typedef

Typedefs allow us to define types with a different name - which can come in handy when dealing with structs and pointers.

typedef struct {
int x;
int y;
} point;

point p;

Nested Structures

struct complex {
int imag;
float real;
};

struct number {
struct complex comp;
int integers;
} num1, num2;

num2.comp.imag = 11;

union

Unions are like structures except that all the variables share the same memory. When a union is declared the compiler allocates enough memory for the largest data-type in the union. It’s like a giant storage chest where you can store one large item, or a small item, but never the both at the same time

Alignment & Padding

Alignment

Each type except char has an alignment requirement. Signed or unsigned makes no difference.

  • **1-byte **chars can start on any byte address
  • 2-byte shorts must start on an even address,
  • 4-byte ints or floats must start on an address divisible by 4
  • 8-byte longs or doubles must start on an address divisible by 8.

Self-alignment makes access faster because it facilitates generating single-instruction fetches and puts of the typed data.

Padding

// initially
char *p;
char c;
int x;

// Equal to
char *p; /* 4 or 8 bytes */
char c; /* 1 byte */
char pad[3]; /* 3 bytes */
int x; /* 4 bytes */

Structure Alignment & Padding

struct foo1 {
char *p;
char c;
long x;
};

// is equivalent to
struct foo1 {
char *p; /* 8 bytes */
char c; /* 1 byte
char pad[7]; /* 7 bytes */
long x; /* 8 bytes */
};
struct foo3 {
char *p; /* 8 bytes */
char c; /* 1 byte */
};

// is equivalent to
struct foo3 {
char *p; /* 8 bytes */
char c; /* 1 byte */
char pad[7];
};

Bitfields

struct foo6 {
short s;
char c;
int flip:1;
int nybble:4;
int septet:7;
};

// is equal to
struct foo6 {
short s; /* 2 bytes */
char c; /* 1 byte */
int flip:1; /* total 1 bit */
int nybble:4; /* total 5 bits */
int pad1:3; /* pad to an 8-bit boundary */
int septet:7; /* 7 bits */
int pad2:25; /* pad to 32 bits */
};

lib

Static Lib

- include
- head.h
- main.c
- lib
- libcalc.a
- src
- add.c
- sub.c
- mult.c
- divd.c


# Create object files
cd src
gcc -c add.c sub.c mult.c divd.c -I ./include
# Create static lib file
ar rcs libcalc.a add.o sub.o mult.o divd.o
# Use static lib file
gcc main.c -o main.out -I ./include -l calc -L ./lib

Shared Lib


# Create object files
cd src
gcc -c -fpic/-fPIC a.c b.c
# Create shared lib
gcc -shared a.o b.o -o libcalc.so
# Use shared lib
gcc main.c -o main.out -I ./include -l calc -L ./lib

-fpic:Make path as relative path.

Problem

NOT FOUND
When gcc start linking, only the infomation of shared lib is linked into the executable file instead of the actual code of shared lib.
image.png

Solution

  • 设置环境变量
    • export LD_LIBRARY_PATH=$LA_LIBRARY_PATH:/home/xxx/code/lib
    • 放bashrc
  • 设置/etc/ld.so.cache文件
    • 添加路径到ld.so.conf
    • ldconfig更新
  • 设置/liband /usr/lib(不推荐)

Difference

  • static lib will be linked into the excutable file, but shared lib does not.
  • different on the producing method.

Static lib

pros:

  • static lib被打包到应用程序中加载速度快
  • 发布程序无需提供静态库,移植方便

cons:

  • 消耗系统资源,浪费内存 (如下图,会有两份重复的代码)

image.png

  • 更新·部署·发布麻烦 (重新编译)

Shared Lib

pros:

  • 可以实现进程间的资源共享(共享库)
  • 更新·部署·发布简单
  • 可以控制何时加载动态库

cons:

  • 加载速度比static lib 稍慢
  • 发布程序时需要提供依赖的动态库

image.png