Create an "array" whose size is determined at runtime?
Create an "array" whose size is determined at runtime?
malloc and freeDeclared in <stdlib.h>.
void *malloc(size_t size);
Allocates size bytes of uninitialized storage on heap.
If allocation succeeds, returns the starting address of the allocated memory block.
If allocation fails, a null pointer is returned.
size_t: A type that can hold the size (number of bytes) of any object. It is
<stddef.h>, andmalloc and freeDeclared in <stdlib.h>.
void *malloc(size_t size);
T *ptr = malloc(sizeof(T) * n); // sizeof(T) * n bytes
for (int i = 0; i != n; ++i)
ptr[i] = /* ... */
// Now you can use `ptr` as if it points to an array of `n` objects of type `T`
// ...
free(ptr);
To avoid memory leaks, the starting address of that block memory must be passed to free when the memory is not used anymore.
malloc and freeDeclared in <stdlib.h>.
void free(void *ptr);
Deallocates the space previously allocated by an allocation function (such as malloc).
If ptr is a null pointer, this function does nothing.
free!The behavior is undefined if ptr is not equal to an address previously returned by an allocation function.
malloc and freeDeclared in <stdlib.h>.
void free(void *ptr);
Deallocates the space previously allocated by an allocation function (such as malloc).
The behavior is undefined if the memory area referred to by ptr has already been deallocated.
free" is undefined behavior (and often causes severe runtime errors).After free(ptr), ptr no longer points to an existing object, so it is no longer dereferenceable.
malloc and freeWe can also create one single object dynamically (on heap):
int *ptr = malloc(sizeof(int));
*ptr = 42;
printf("%d\n", *ptr);
// ...
free(ptr);
But why? Why not just create one normal variable like int ival = 42;?
malloc and freeBenefit: The lifetime of a dynamically allocated object goes beyond a local scope.
It is not destroyed until we free it.
int *create_array(void) {
int a[N];
return a; // Returns the address of the local object `a`.
// When the function returns, `a` will be destroyed, so that
// the returned address becomes invalid.
// Dereferencing the returned address is undefined behavior.
}
int *create_dynamic_array(int n) {
return malloc(sizeof(int) * n); // OK. The allocated memory is valid until
// we free it.
}
malloc and freeCreate a "2-d array" on heap?
malloc and freeCreate a "2-d array" on heap? - Another way: Create a 1-d array of length n * m.
int *p = malloc(sizeof(int) * n * m);
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
p[i * m + j] = /* ... */ // This is the (i, j)-th entry.
// ...
free(p);
callocDeclared in <stdlib.h>
void *calloc(size_t num, size_t each_size);
Allocates memory for an array of num objects (each of size each_size), and initializes all bytes in the allocated storage to zero
Similar as malloc(num * each_size).
malloc, calloc and freeThe behaviors of malloc(0), calloc(0, N) and calloc(N, 0) are implementation-defined:
freed.mallocAn array has limited lifetime (unless it is global or static). It is destroyed when control reaches the end of its scope.
Objects allocated by malloc are not destroyed until their address is passed to free.
The program crashes if the size of an array is too large (running out of stack memory). There is no way of recovery.
Attempt to malloc a block of memory that is too large results in a null pointer. We can know if there is no enough heap memory by doing a null check.
int *ptr = malloc(1ull << 60); // unrealistic size
if (!ptr)
report_an_error("Out of memory.");
Pointer to const
const thinks that it is pointing to a const variable (though it may not), so it prevents you from modifying the pointed-to variable through it.const whenever possible.void *
Dynamic memory
void *malloc(size_t size);
void *calloc(size_t num, size_t each_size);
void free(void *ptr);
malloc: Allocates size bytes of uninitialized memory and returns its starting address.
calloc: Allocates num * each_size bytes of memory
Both malloc and calloc return a null pointer on failure.
free: Deallocates the memory block starting at ptr.
C does not have a special construct for "string".
A string is a sequence of characters stored contiguously. We often use an array or a pointer to the first character to represent a string.
'\0' at the end.char s[10] = "abcde"; // s = {'a', 'b', 'c', 'd', 'e', '\0'}
printf("%s\n", s); // prints abcde
printf("%s\n", s + 1); // prints bcde
s[2] = ';'; // s = "ab;de"
printf("%s\n", s); // prints ab;de
s[2] = '\0';
printf("%s\n", s); // prints ab
The position of the first '\0' is the end of the string. Anything after that is discarded.
'\0''\0' is the "null character" whose ASCII value is 0.
It is the only way to mark the end of a C-style string.
Every standard library function that handles strings will search for '\0' in that string.
'\0', they will search nonstop, and eventually go out of range (undefined behavior).char s[5] = "abcde"; // OK, but no place for '\0'.
printf("%s\n", s); // undefined behavior (missing '\0')
Remember to allocate one more byte storage for '\0'!
An empty string contains no characters before the null character.
char empty[] = ""; // `empty` is of type char[1], which contains only '\0'.
printf("%s\n", empty); // Prints only a newline.
printf(""); // Nothing is printed
%s in scanf matches a sequence of non-whitespace characters.
'\0' will be placed at the end.Suppose the input is 123 456:
char str[100] = "abcdef";
scanf("%s", str); // Reads "123". `str` becomes {'1', '2', '3', '\0', 'e', 'f'}
printf("%s\n", str); // Prints "123".
// 'e' and 'f' are not considered as part of the string.
scanf is not memory safe:
char str[10];
scanf("%s", str);
str is decayed (implicitly converted) to char * when passed as an argument.scanf receives only a pointer char *. It has no idea how big the array is.That's why it is banned by MSVC. An alternative is to use scanf_s, but not necessarily supported by every compiler.
gets reads a string without bounds checking. It has been removed since C11.
gets that does bounds checking is gets_s, but not supported by every compiler.The best alternative: fgets. It is more portable, more generic, and safer (with bounds checking).
char str[100];
fgets(str, 100, stdin);
puts(str): Prints the string str, followed by a newline.
Homework Read the cppreference documentation for fgets. Answer the following questions:
Some common standard library functions: declared in <string.h>.
strlen(str): Returns the length of the string str.strcpy(dest, src): Copies the string src to dest.strcat(dest, src): Appends a copy of src to the end of dest.strcmp(s1, s2): Compares two strings in lexicographical order.strchr(str, ch): Finds the first occurrence of ch in str.This page is only a brief introduction which cannot be relied on. The detailed documentations can be found here.
Read the documentation of a function before using it.
'\0' counted in strlen?strcpy put a null character at the end? What about strncpy?strcpy(dest, src), what will happen if dest and src refer to the same memory address? What if they overlap? What about strcat?strcmp? Is it true/false?If you use the function without making these clear, you are heading for late-night debugging sessions!
strlenImplement your own strlen, which accepts a pointer to the first character of a string and returns its length.
The length of a string does not count the null character '\0' in.
strlen("hello") is 5, not 6.strlenImplement your own strlen, which accepts a pointer to the first character of a string and returns its length.
size_t my_strlen(const char *str) {
size_t ans = 0;
while (*str != '\0') {
++ans;
++str;
}
return ans;
}
size_t my_strlen(const char *str) {
const char *end = str;
while (*end != '\0')
++end;
return end - str;
}
strlenImplement your own strlen, which accepts a pointer to the first character of a string and returns its length.
size_t my_strlen(const char *str) {
size_t ans = 0;
while (*str++ != '\0') // Understand and learn to use this.
++ans;
return ans;
}
strlenThere is no magic in strlen. It traverses the entire string and counts the characters, which is very slow.
Do not call strlen repeatedly!
for (size_t i = 0; i < strlen(s); ++i) // very slow
// ...
i < strlen(s) is evaluated, which causes the entire string to be traversed.strlenThere is no magic in strlen. It traverses the entire string and counts the characters, which is very slow.
Do not call strlen repeatedly!
for (size_t i = 0; i < strlen(s); ++i) // very slow
// ...
Change it to:
int n = strlen(s);
for (int i = 0; i < n; ++i)
// ...
Full list can be found here.
strtol, strtoll, strtoul, strtoull, strtof, strtod, strtold: Extracts the numeric value from a string.
atoi, atol, ...: The base can be customized, and they have better error-reporting mechanisms.A string literal is something like "abcde", surrounded by double quotes ".
char [N+1], where N is the length of the string.
+1 is for the terminating null character.const char [N+1], which is more reasonable.When initializating a pointer with a string literal,
char *p = "abcde";
we are actually letting p point to the address of the string literal.
Using a pointer to non-const to point to a string literal is allowed in C (not allowed in C++), but very dangerous:
char *p = "abcde"; // OK
p[3] = 'a'; // No compile-error, but undefined behavior,
// and possibly severe runtime-error.
Correct ways:
Use low-level constness to protect it:
const char *str = "abcde";
str[3] = 'a'; // compile-error
Copy the contents into an array:
char arr[] = "abcde";
arr[3] = 'a'; // OK.
// `arr` contains a copy of "abcde".
const char *translations[] = {
"zero", "one", "two", "three", "four",
"five", "six", "seven", "eight", "nine"
};
A C-style string is a sequence of characters stored contiguously, with '\0' at the end.
'\0'.const.
const to point to a string literal.scanf/printf, fgets, putsstrlen, strcmp, strcpy, ...strtol, strtoll, ...Implement your own strlen, strchr, strcmp, strcpy and strcat. This will be in homework.
0.0 for floating point or null pointer value for pointers, although it does for most modern computers and compilers.
num * each_size.