C doesn't have built-in iterators, but function pointers can easily be utilised for this purpose. The following example iterates over the lines of a string, copies them into a temporary buffer and calls the given function. If you have really long lines, you should use heap allocation instead of stack.
void pretty_print(char * s) {
printf("[%s]\n", s);
}
void iterate_lines(const char * text, void (*fp)(char*)) {
size_t textlen = strlen(text);
size_t index = 0;
while(index < textlen) {
size_t lineend = index;
while(text[lineend] != 10 && text[lineend] != 13 && text[lineend] != 0 ) lineend++;
size_t linelen = lineend - index;
char line[linelen + 1]; // or malloc(linelen + 1), but don't forget free()
memcpy(line, text + index, linelen);
line[linelen] = '\0';
(*fp)(line);
index += text[linelen] == 13 ? linelen + 2 : linelen + 1; // unix (LF) and dos (CR LF) line-endings are also covered
}
}
int main(void) {
char * text = "Line one\nLine two\n\nLine four";
iterate_lines(text, pretty_print);
return EXIT_SUCCESS;
}
If you don't like macros, and you use modern C*, you can try flexible array members.
typedef struct {
size_t length;
char str[];
} String;
String * string_init(const char * initstr) {
if (inistr == NULL) return NULL;
size_t len = strlen(initstr);
String * str = malloc(sizeof(String) + len + 1);
if (str == NULL) return NULL;
str->length = len;
strcpy(str->str, initstr);
return str;
}
int main(void) {
String * greeting = string_init("Hello!");
printf("Lenght: %zu, text: '%s'\n", greeting->length, greeting->str);
return EXIT_SUCCESS;
}
* The C standard is C17 now (and soon will be C23), not the C89/C90. Those old standards has been withdrawn by both ANSI/INCITS and ISO/IEC.
A major caveat when using GCC __cleanup__ attribute: always initialize your pointers, even with a NULL. Otherwise you could end up with undefined behaviour (actually a "double free or corruption" error message).
void print_positive_result(int i) {
char * CLEANUPCHAR(s); // without initialization it holds a random value from the stack
if (i >= 0) {
s = malloc(32);
snprintf(s, 32, "The result: %d", i);
puts(s);
} else {
puts("Value error.");
}
return; // with a negative parameter it calls free() to a random memory address - UB
}
Actually, structs aren't even necessary for generics in C.
#define GENERIC_INIT(T) uint8_t T[sizeof(size_t)] = {0};
#define GENERIC_VALUE(T, type) *((type*)T)
GENERIC_INIT(t);
GENERIC_VALUE(t, int) = 12;
printf("Integer value: %d\n", GENERIC_VALUE(t, int));
GENERIC_VALUE(t, float) = 1.5;
printf("Float value: %f\n", GENERIC_VALUE(t, float));
GENERIC_VALUE(t, char*) = "Hello, world!";
printf("Pointer value: %s\n", GENERIC_VALUE(t, char*));
The GCC compiler has a nice C language attribute that helps you relieve the pain of manual memory management. If a variable you declared with __cleanup__ goes out of scope, the specified function will be called with a pointer to the variable, as argument.
#define CLEANUPCHAR(x) __attribute__((__cleanup__(free_char_buffer))) x
void free_char_buffer(char ** buffer) {
free(*buffer);
}
void print_result(int i) {
char * CLEANUPCHAR(s) = malloc(32);
snprintf(s, 32, "The result: %d", i);
puts(s);
return;
}
The Pascal programming language has length-prefixed string type. Let's try it in C.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define CSTR(lps) lps + sizeof(size_t)
#define CLEN(lps) *((size_t *)lps)
char * new_length_prefixed_string(const char * s) {
if (s == NULL) return NULL;
size_t len = strlen(s);
char * lps = calloc(sizeof(size_t) + len + 1, 1);
if (lps == NULL) return NULL;
memcpy(CSTR(lps), s, len);
CLEN(lps) = len;
return lps;
}
int main(void) {
char * lps = new_length_prefixed_string("Hello!");
if (lps == NULL) return -1;
printf("String: %s\nLength: %zu\n", CSTR(lps), CLEN(lps));
free(lps);
return EXIT_SUCCESS;
}
Just a simple struct and macro trick.
#include <stdint.h>
#include <stdio.h>
#define GENERIC_VALUE(generic, type) *((type*)generic.value)
typedef struct {
uint8_t value[8]; // 8 bytes for 64 bit values
} Generic;
int main(void) {
Generic T = {0};
GENERIC_VALUE(T, int) = 10;
printf("int: %d\n", GENERIC_VALUE(T, int));
GENERIC_VALUE(T, float) = 9.9999F;
printf("float: %f\n", GENERIC_VALUE(T, float));
GENERIC_VALUE(T, char*) = "Hello!";
printf("string: %s\n", GENERIC_VALUE(T, char*));
return EXIT_SUCCESS;
}
"Starting in Windows 10 build 17134 (April 2018 Update), the Universal C Runtime supports using a UTF-8 code page. This means that char strings passed to C runtime functions will expect strings in the UTF-8 encoding. To enable UTF-8 mode, use "UTF-8" as the code page when using setlocale. For example, setlocale(LC_ALL, ".utf8") will use the current default Windows ANSI code page (ACP) for the locale and UTF-8 for the code page. After calling setlocale(LC_ALL, ".UTF8"), you may pass "😊" to mbtowcs and it will be properly translated to a wchar_t string, whereas previously there was not a locale setting available to do this." – https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale
My PinePhone Manjaro Community Edition phone has just arrived. It's a massive dissapointment. The hardware is way below expectations, the available operating systems and software are pre-alpha quality. $200 + shipping cost are down the drain...
Using wide characters and the unicode enabled functions is Win32, you can display proper texts in the cmd shell regardless of codepage settings.
wchar_t buffer[16]; StringCchCopyW(buffer, 16, L"Őrß"); HANDLE std = GetStdHandle(STD_OUTPUT_HANDLE); WriteConsoleW(std, buffer, wcslen(buffer), NULL, NULL); CloseHandle(std);