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);