Passing an array between functions - c

I'm working on this program to convert bases of numbers to a new base. Primarily by converting the first base to 10, then converting ten to the new base. The actual conversions are working and when I pass it to main, it reads it correctly. However, when i pass 'charArray' to the 'findAnswer' function, it won't print out the values and my program crashes. so it's not being passed correctly I assume. Does anybody see the error I am making?
Output: 1101 [...then program crashes]
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
char** buildAnsArray(FILE *ifp);
char* baseConversion(int num, int base, int newBase);
char* pow2Converter(int num, int base, int newBase);
int findAnswer(char **answerArray, char **charArray, int high, int low);
int main()
{
int num, base, newBase;
char **answerArray;
char *answerBaseConversion;
char *answerPowConversion;
char charArray[10][10];
int i = 0;
FILE *ifp;
ifp = fopen("data.txt", "r");
answerArray = buildAnsArray(ifp);
while(fscanf(ifp, "%d %d %d\n", &num, &base, &newBase)!=EOF){
answerBaseConversion = baseConversion(num, base, newBase);//baseConversion(num, base, newBase);
strcpy(charArray[i],answerBaseConversion);
i++;
}
findAnswer(answerArray, charArray, i-1, 0);
free(answerArray);
fclose(ifp);
return 0;
}
char** buildAnsArray(FILE *ifp){
char *answerPtr;
int numConversions;
int i = 0; int j = 0;
char **answerArray;
char answerString[10];
fscanf(ifp, "%d\n", &numConversions);
answerArray = malloc(numConversions * sizeof(char*));
for (i = 0; i < numConversions; i++){
answerArray[i] = malloc(10 * sizeof(char));
}
while(j < numConversions){
fscanf(ifp, "%s\n", &answerString);
strcpy(answerArray[j],answerString);
j++;
}
return answerArray;
}
char* baseConversion(int num, int base, int newBase){
if(base == 10){
char a[2];
switch(num%newBase){
case 0:
a[0] = '0';
break;
case 1:
a[0] = '1';
break;
case 2:
a[0] = '2';
break;
case 3:
a[0] = '3';
break;
case 4:
a[0] = '4';
break;
case 5:
a[0] = '5';
break;
case 6:
a[0] = '6';
break;
case 7:
a[0] = '7';
break;
case 8:
a[0] = '8';
break;
case 9:
a[0] = '9';
break;
case 10:
a[0] = 'A';
break;
case 11:
a[0] = 'B';
break;
case 12:
a[0] = 'C';
break;
case 13:
a[0] = 'D';
break;
case 14:
a[0] = 'E';
break;
case 15:
a[0] = 'F';
break;
}
a[1] = '\0';
char *str;
if (num/newBase != 0)
str = baseConversion(num/newBase, base, newBase);
else{
str = malloc(11 * sizeof(char));
str[0] = '\0';
}
strcat(str, a);
return str;
}
else if(newBase==10){
int num3;
num3 = (((((((num/1000)*base)+((num%1000)/100))*base) + (((num%1000)%100)/10)) *base) + (((num%1000)%100)%10));
}
else{
char *str2 = baseConversion(num, base, 10);
int num2 = atoi(str2);
return baseConversion(num2, 10, newBase);
}
}
int findAnswer(char **answerArray, char **charArray, int high, int low){
printf("%s",answerArray[high]);
printf("%s", charArray[low]);
if(strcmp(answerArray[high], charArray[low])==0){
printf("WOO");
return low;//return to a string
}
else{
if (low == high)
return 0;//return to string? or recursion
else{
printf("hi");
return findAnswer(answerArray, charArray, high, low+1);//instead of returnning to output, return to a string.
}
}
}

Related

Custom print function has undesired behaviour

I'm making my own x86 OS using the i386-elf cross-compiler and linker and nasm to compile asm files. The OS itself runs with qemu. That being said, I made a custom print function but ran into a problem. Every time I access memory (either through the [] operator or by dereferencing a pointer) and call my print function afterwards, it leaves 8 blank spaces and then prints normally.
Print code:
void printv(char *str, ...)
{
unsigned int tmp_cursor = get_cursor_position();
cursor_position.x = (unsigned short)(tmp_cursor >> 16);
cursor_position.y = (unsigned short)tmp_cursor;
char buffer[12];
va_list list_ptr;
va_start(list_ptr, str);
unsigned int i = 0;
for (char *ptr = str; *ptr != '\0'; ptr++)
{
switch (*ptr)
{
case '%':
cursor_position.y += (cursor_position.x + i) / 80;
cursor_position.x = (cursor_position.x + i) % 80;
update_cursor(cursor_position.x, cursor_position.y);
i = 0;
switch (*(ptr + 1))
{
case 'c':
buffer[0] = (char)va_arg(list_ptr, int);
buffer[1] = '\0';
printv(buffer);
ptr++;
break;
case 's':
printv(va_arg(list_ptr, char *));
ptr++;
break;
case 'i':
case 'd':
int_to_str(va_arg(list_ptr, int), buffer, 10);
printv(buffer);
ptr++;
break;
default:
*(char*)(0xb8000 + (cursor_position.x + i + cursor_position.y * 80) * 2) = *ptr;
i++;
break;
}
break;
case '\n':
i = 0;
cursor_position.x = 0;
cursor_position.y++;
break;
case '\t':
cursor_position.y += (cursor_position.x + i) / 80;
cursor_position.x = (cursor_position.x + i) % 80;
update_cursor(cursor_position.x, cursor_position.y);
i = 0;
cursor_position.x += TAB_SPACE - cursor_position.x % TAB_SPACE - 1;
break;
default:
*(char *)(0xb8000 +(cursor_position.x + i + cursor_position.y * 80) * 2) = *ptr;
i++;
break;
}
}
va_end(list_ptr);
memset(buffer, '\0', 12);
cursor_position.y += (cursor_position.x + i) / 80;
cursor_position.x = (cursor_position.x + i) % 80;
update_cursor(cursor_position.x, cursor_position.y);
}
Call example:
printv("Starting PonchOS!\n");
char str[12];
for (int i = 0; i < 11; i++)
{
str[i] = 'a' + i;
}
str[11] = '\0';
printv("Testtesttesttesttest");
Output:
As you can see, it prints fine before any memory access, but after that, it leaves those white spaces. Any ideas as to why this happens?
Edit:
Implementing #chqrlie 's changes, some issues have been fixed, although spacing problems persist.
Code:
printv("Starting PonchOS!\n");
printv("%c\n", 'C');
printv("%i", 128);
printv("%s", "string");
Output:
The problem comes from your not updating the cursor variables consistently when calling printv recursively. Furthermore you would get undefined behavior for this call: printv("%s", "%s").
You should split the function into a high level one that handles the formatting and a low level one that draws a string to the screen.
Here is a modified version:
void putstr(const char *str, size_t n) {
if (n > 0) {
unsigned int tmp_cursor = get_cursor_position();
int x = (unsigned short)(tmp_cursor >> 16);
int y = (unsigned short)tmp_cursor;
size_t i;
for (i = 0; i < n; i++) {
switch (str[i]) {
case '\n':
y += x / 80 + 1;
x = 0;
break;
case '\r':
y += x / 80;
x = 0;
break;
case '\t':
x = (x + TAB_SPACE) / TAB_SPACE * TAB_SPACE;
y += x / 80;
x %= 80;
break;
default:
*(char *)(0xb8000 + (y * 80 + x) * 2) = str[i];
x++;
break;
}
}
update_cursor(x, y);
}
}
void printv(const char *str, ...) {
char buffer[32];
char *p;
const char *ptr;
va_list list_ptr;
va_start(list_ptr, str);
for (ptr = str; *ptr != '\0'; ptr++) {
if (*ptr == '%' && ptr[1] != '\0') {
putstr(str, ptr - str);
str = ptr;
ptr++;
switch (*ptr) {
case 'c':
buffer[0] = (char)va_arg(list_ptr, int);
putstr(buffer, 1);
str += 2; // skip the format
break;
case 's':
p = va_arg(list_ptr, char *);
putstr(p, strlen(p));
str += 2; // skip the format
break;
case 'i':
case 'd':
int_to_str(va_arg(list_ptr, int), buffer, 10);
putstr(buffer, strlen(buffer));
str += 2; // skip the format
break;
case '%':
str += 1; // skip the initial %
break;
}
}
}
putstr(str, ptr - str);
va_end(list_ptr);
}

Mistake in custom implementation of printf

I'm trying to implement my own version of printf and I'm having trouble when I need to print an argument of the form %pd, where p is the number of characters to be printed.
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
void print(char *, ...);
char *convert(unsigned int, int);
char *to_lower(unsigned int, int);
unsigned int count_digits(unsigned int);
int main()
{
char str[12]="World";
char c='A';
int n = 5, x= 1011, y = 12, z = 5, t = 10;
print("Hello s c |%s| |%c| 5 |%d| 5 some random %X %x %b %o words\n", str, c, n, x, y, z, t);
print("%5d\n", y);
}
void print(char *format, ...)
{
int num, precision = 0, nDigits, i, temp;
char *num_deca;
va_list lst;
va_start(lst, format);
while(*format != '\0')
{
if(*format != '%')
{
putchar(*format);
format++;
continue;
}
format++;
if(isdigit(*format))
{
temp = atoi(format);
precision = precision*10+temp;
//precision = atoi(format);
format++;
continue;
}
// format++;
switch(*format)
{
case 'c':
putchar(va_arg(lst, int));
break;
case 'd':
num = (va_arg(lst, int));
//nDigits = count_digits(num);
if(num > 0)
{
num_deca = convert(num, 10);
nDigits = count_digits(num);
for(i = 0; i < (precision - nDigits); i++)
{
putchar(' ');
}
fputs(num_deca, stdout);
// fputs(convert(num,10), stdout);
}
else
{
num = -num;
putchar('-');
fputs(convert(num,10), stdout);
}
break;
case 's':
fputs(va_arg(lst, char *), stdout);
break;
case 'X':
num = va_arg(lst, int);
fputs(convert(num,16), stdout);
break;
case 'x':
num = va_arg(lst, int);
fputs(to_lower(num,16), stdout);
break;
case 'b':
num = va_arg(lst, int);
fputs(convert(num,2), stdout);
break;
case 'o':
num = va_arg(lst, int);
fputs(convert(num,8), stdout);
break;
}
format++;
}
va_end(lst);
}
char *convert(unsigned int num, int base)
{
static char Representation[]= "0123456789ABCDEF";
static char buffer[50];
char *ptr;
ptr = &buffer[49];
*ptr = '\0';
do
{
*--ptr = Representation[num%base];
num /= base;
}while(num != 0);
return(ptr);
}
char *to_lower(unsigned int num, int base)
{
static char Representation[]= "0123456789abcdef";
static char buffer[50];
char *ptr;
ptr = &buffer[49];
*ptr = '\0';
do
{
*--ptr = Representation[num%base];
num /= base;
}while(num != 0);
return(ptr);
}
unsigned int count_digits(unsigned int n)
{
unsigned int counter = 0;
while(n != 0)
{
n = n/10;
counter++;
}
return counter;
}
Output: Hello s c |World| |A| 5 |5| 5 some random 3F3 c 101 12 words
d
So, the basic cases are working fine, the only issue I'm encountering is on the second print. The number 12 with %5d% should be whitespacewhitespacewhitespace12 (since 12 only has 2 digits). I computed the number of digits of the argument and used a for loop that goes until the difference between the precision and the number of digits, but then the program doesn't consider d as being part of the format specifier anymore and just prints the letter 'd'. Any idea why?
The problem is that you print every character until you find a '%'. If you find a '%' you go to the next character and process it. However, if the next character is a digit, you modify precission and make a 'continue' to the start of the loop. When re-starting the loop, the next character (a 'd') is not a '%', so it just get printed as is. You should have a variable that tells you if you are still processing a sequence, or either don't go to the start of the loop before you end the processing of the full sequence ('%5d').

How do I use free to deallocate heap allocations made using malloc?

I've encountered an issue with heap deallocation using free() in my tokenizer. The tokenizer is part of a recursive descent parsing calculator, which works flawlessly otherwise. But upon incorporation of a call to the deallocation function, it behaves erratically. While realistically, the calculator will likely never come close to exhausting its heap, writing a program with a memory leak is just poor practice.
tokenize.h
#define OPERAND 0
#define OPERATOR 1
#define PARENTHESIS 2
#define TERMINAL 3
#define ADD '+'
#define SUBTRACT '-'
#define MULTIPLY '*'
#define DIVIDE '/'
#define EXPONENT '^'
#define L_PARENTHESIS '('
#define R_PARENTHESIS ')'
typedef struct {
int id;
char *value;
} token;
int token_count();
token *tokenize();
void deallocate();
tokenize.c
#include <stdio.h>
#include <stdlib.h>
#include "tokenize.h"
int token_count(char string[]) {
int i = 0;
int count = 0;
while (string[i] != '\0') {
if (string[i] >= '0' && string[i] <= '9') {
while (1) {
i++;
if (string[i] >= '0' && string[i] <= '9') {
continue;
} else {
break;
}
}
count++;
continue;
}
switch (string[i]) {
case ADD:
case SUBTRACT:
case MULTIPLY:
case DIVIDE:
case EXPONENT:
case L_PARENTHESIS:
case R_PARENTHESIS:
count++;
i++;
continue;
default:
return 0;
break;
}
}
return count;
}
token *tokenize(char string[]) {
int i = 0;
token *ret;
int count = token_count(string);
if (!count) {
return ret;
}
ret = malloc((count + 1) * sizeof(token));
ret[count].id = TERMINAL;
int ret_ind = 0;
while (string[i] != '\0') {
if (string[i] >= '0' && string[i] <= '9') {
ret[ret_ind].id = OPERAND;
int size = 0;
int j = i;
while (1) {
size++;
j++;
if (string[j] >= '0' && string[j] <= '9') {
continue;
} else {
break;
}
}
ret[ret_ind].value = malloc(size * sizeof(char) + 1);
ret[ret_ind].value[size + 1] = '\0';
for(int k = 0; k < size; k++) {
ret[ret_ind].value[k] = string[i + k];
}
i = j;
ret_ind++;
continue;
}
switch (string[i]) {
case ADD:
case SUBTRACT:
case MULTIPLY:
case DIVIDE:
case EXPONENT:
ret[ret_ind].id = OPERATOR;
ret[ret_ind].value = malloc(2 * sizeof(char));
ret[ret_ind].value[0] = string[i];
ret[ret_ind].value[1] = '\0';
ret_ind++;
i++;
continue;
case L_PARENTHESIS:
ret[ret_ind].id = PARENTHESIS;
ret[ret_ind].value = malloc(2 * sizeof(char));
ret[ret_ind].value[0] = L_PARENTHESIS;
ret[ret_ind].value[1] = '\0';
ret_ind++;
i++;
continue;
case R_PARENTHESIS:
ret[ret_ind].id = PARENTHESIS;
ret[ret_ind].value = malloc(2 * sizeof(char));
ret[ret_ind].value[0] = R_PARENTHESIS;
ret[ret_ind].value[1] = '\0';
ret_ind++;
i++;
continue;
default:
break;
}
break;
}
return ret;
}
void deallocate(token *in) {
int i = 0;
while (1) {
free(in[i].value);
i++;
if (in[i].id == TERMINAL) {
break;
}
}
free(in);
return;
}
There are multiple problems in your code:
in case the input line has no tokens or a syntax error, you return ret uninitialized from tokenize. You should return NULL instead.
ret[ret_ind].value[size + 1] = '\0'; stores the null terminator one step too far in the allocated array. It should be ret[ret_ind].value[size] = '\0';
malloc(size * sizeof(char) + 1) is inconsistent: if you insist on using sizeof(char), which is 1 by definition, you should write malloc((size + 1) * sizeof(char)), but it is idiomatic to use malloc(size + 1) in C and you could also replace multiple lines of code with a simple ret[ret_ind].value = strndup(string + i, k);
the cases for L_PARENTHESIS and R_PARENTHESIS could be merged into a single block.
the deallocation loop should stop when you reach the TERMINAL token. As currently coded, you cannot handle an empty list, which you should not produce, but it is better to make utility functions more resilient to later changes.
void deallocate(token *in) {
if (in) {
for (int i = 0; in[i] != TERMINAL; i++)
free(in[i].value);
free(in);
}
}
the prototypes in token.h should include the typed argument lists.
Here is a simplified version:
#include <stdio.h>
#include <stdlib.h>
#include "tokenize.h"
int token_count(const char *string) {
int count = 0;
int i = 0;
while (string[i] != '\0') {
switch (string[i++]) {
case ' ':
continue;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
i += strspn(string + i, "0123456789");
continue;
case ADD:
case SUBTRACT:
case MULTIPLY:
case DIVIDE:
case EXPONENT:
case L_PARENTHESIS:
case R_PARENTHESIS:
count++;
continue;
default:
return -1;
}
}
return count;
}
token *tokenize(const char *string) {
int count = token_count(string);
if (count <= 0)
return NULL;
token *ret = malloc((count + 1) * sizeof(token));
int i = 0;
int ret_ind = 0;
while (string[i] != '\0') {
if (string[i] >= '0' && string[i] <= '9') {
int size = strspn(string + i, "0123456789");
ret[ret_ind].id = OPERAND;
ret[ret_ind].value = strndup(string + i, size);
ret_ind++;
i += size;
continue;
}
switch (string[i]) {
case ' ':
i++;
continue;
case ADD:
case SUBTRACT:
case MULTIPLY:
case DIVIDE:
case EXPONENT:
ret[ret_ind].id = OPERATOR;
ret[ret_ind].value = malloc(2);
ret[ret_ind].value[0] = string[i];
ret[ret_ind].value[1] = '\0';
ret_ind++;
i++;
continue;
case L_PARENTHESIS:
case R_PARENTHESIS:
ret[ret_ind].id = PARENTHESIS;
ret[ret_ind].value = malloc(2);
ret[ret_ind].value[0] = string[i];
ret[ret_ind].value[1] = '\0';
ret_ind++;
i++;
continue;
default:
break;
}
break;
}
ret[ret_ind].id = TERMINAL;
return ret;
}
void deallocate(token *in) {
if (in) {
for (int i = 0; in[i] != TERMINAL; i++)
free(in[i].value);
free(in);
}
}
Here are additional remarks for the rest of the code:
why clear the screen on entry and exit?
you should test for end of file in the main loop:
if (!fgets(user_in, 1024, stdin))
break;
you should strip the newline efficiently:
#include <string.h>
user_in[strcspn(user_in, "\n")] = '\0';
then you can simplify the test for exit:
if (!strcmp(user_in, "exit"))
break;
no need to clear user_in after solve()
you could simplify testing by solving the command line arguments:
for (int i = 1; i < argc; i++)
solve(argv[i]);
you should ignore white space and accept empty lines
you should use "%.17g instead of %lf. Note that the l is mandatory
for scanf() for a double type, but ignored for printf, because
float arguments are converted to double when passed to vararg
functions like printf.
you should use a context structure and pass a pointer to it
to parse and its helper functions to avoid global variables
as you can see in try_add_sub and try_mul_div, it would simplify
the switch to unify token types and avoid the OPERATOR classification.
the parser is too complicated: you should use recursive descent more
directly: try_add_sub should first call try_mul_div and iterate on
additive operators, calling try_mul_div for each subsequent operand.
Similarly, try_mul_div should first call try_exp and try_exp would
call try_primitive which would handle parentheses and constants.
this approach consumes one token at a time, which can be read from
the expression source on the fly, bypassing the need for tokenizing the whole string.
you should accept the full number syntax for constants, which is easy with strtod().
Here is a simplified version along these directions:
//---- tokenize.h ----
#define TERMINAL 0
#define OPERAND 1
#define ERROR 2
#define ADD '+'
#define SUBTRACT '-'
#define MULTIPLY '*'
#define DIVIDE '/'
#define EXPONENT '^'
#define L_PARENTHESIS '('
#define R_PARENTHESIS ')'
#define SYNTAX_ERROR 1
#define PAREN_ERROR 2
typedef struct context {
char *p;
char *nextp;
int parenthesis_balance;
int error_code;
double value;
} context;
int this_token(context *cp);
void skip_token(context *cp);
//---- tokenize.c ----
#include <stdlib.h>
//#include "tokenize.h"
int this_token(context *cp) {
char *p = cp->p;
for (;;) {
switch (*p) {
case '\0':
cp->nextp = p;
return TERMINAL;
case ' ':
case '\t':
case '\n':
/* ignore white space */
p++;
continue;
case ADD:
case SUBTRACT:
case MULTIPLY:
case DIVIDE:
case EXPONENT:
case L_PARENTHESIS:
case R_PARENTHESIS:
/* single character operators */
cp->nextp = p + 1;
return *p;
default:
/* try and parse as a number constant */
cp->value = strtod(p, &cp->nextp);
if (cp->nextp > p)
return OPERAND;
return ERROR;
}
}
}
void skip_token(context *cp) {
cp->p = cp->nextp;
}
//---- parse.h ----
int parse(char expression[], double *result);
void solve(char expression[]);
//---- parse.c ----
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
//#include "tokenize.h"
//#include "parse.h"
/* expression parsers return non zero upon error */
int try_add_sub(context *cp, double *result);
int try_mul_div(context *cp, double *result);
int try_exp(context *cp, double *result);
int try_primary(context *cp, double *result);
int try_add_sub(context *cp, double *result) {
if (try_mul_div(cp, result))
return 1;
for (;;) {
double operand;
switch (this_token(cp)) {
case ADD:
skip_token(cp);
if (try_mul_div(cp, &operand))
return 1;
*result += operand;
continue;
case SUBTRACT:
skip_token(cp);
if (try_mul_div(cp, &operand))
return 1;
*result -= operand;
continue;
}
return 0;
}
}
int try_mul_div(context *cp, double *result) {
if (try_exp(cp, result))
return 1;
for (;;) {
double operand;
switch (this_token(cp)) {
case MULTIPLY:
skip_token(cp);
if (try_exp(cp, &operand))
return 1;
*result *= operand;
continue;
case DIVIDE:
skip_token(cp);
if (try_exp(cp, &operand))
return 1;
*result /= operand;
continue;
}
return 0;
}
}
int try_exp(context *cp, double *result) {
if (try_primary(cp, result))
return 1;
if (this_token(cp) == EXPONENT) {
double operand;
skip_token(cp);
if (try_exp(cp, &operand))
return 1;
*result = pow(*result, operand);
}
return 0;
}
int try_primary(context *cp, double *result) {
switch (this_token(cp)) {
case OPERAND:
skip_token(cp);
*result = cp->value;
return 0;
case L_PARENTHESIS:
skip_token(cp);
cp->parenthesis_balance++;
if (try_add_sub(cp, result))
return 1;
cp->parenthesis_balance--;
if (this_token(cp) != R_PARENTHESIS) {
cp->error_code = PAREN_ERROR;
return 1;
}
skip_token(cp);
return 0;
}
cp->error_code = SYNTAX_ERROR;
return 1;
}
/* parse and evaluate an expression, return error code, update result */
int parse(char expression[], double *result) {
context cc;
cc.nextp = cc.p = expression;
cc.parenthesis_balance = 0;
cc.error_code = 0;
cc.value = 0;
if (try_add_sub(&cc, result))
return cc.error_code;
if (this_token(&cc) != TERMINAL)
return SYNTAX_ERROR;
return 0;
}
void solve(char expression[]) {
double result = 0;
switch (parse(expression, &result)) {
case 0:
printf(" %.17g\n", result);
break;
case SYNTAX_ERROR:
printf("ERROR: Syntax\n");
break;
case PAREN_ERROR:
printf("ERROR: Unbalanced parenthesis\n");
break;
}
}
//---- calculator.c ----
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include "parse.h"
int main(int argc, char **argv) {
for (int i = 1; i < argc; i++)
solve(argv[i]);
if (argc == 1) {
char user_in[1024];
char *p;
printf("Terminal Calculator\n");
printf("Type 'exit' to terminate\n\n");
for (;;) {
printf("=> ");
if (!fgets(user_in, sizeof user_in, stdin)) {
printf("\n");
break;
}
/* strip trailing newline */
user_in[strcspn(user_in, "\n")] = '\0';
/* skip initial white space */
p = user_in + strspn(user_in, " \t");
/* ignore empty and comment lines */
if (*p == '\0' || *p == '#')
continue;
/* trap exit command */
if (!strcmp(p, "exit"))
break;
solve(p);
}
}
return 0;
}

c char array to uint8_t array

I have a basic question regarding a c problem I'm having. My input char array would be something like:
'DABC95C1'
and I want to make an uint8_t array out of it
0xDA 0xBC 0x95 0xC1
I have easily access to each char but I dont know how I can form 0xDA. Is there function in c or can i just cast it?
Use the strtoull function to convert a string to a number in a given base. Then just shift out the desired bytes. Such as:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
unsigned long long res = strtoull("DABC95C1", NULL, 16);
printf("%hhx, %hhx, %hhx, %hhx",
(unsigned char)res,
(unsigned char)((res >> 8) & 0xFF),
(unsigned char)((res >> 16) & 0xFF),
(unsigned char)((res >> 24) & 0xFF)
);
return 0;
}
result:
c1, 95, bc, da
Demo
Notes:
As your requirement is to get an array of bytes, you might be tempted to do something like
uint8_t *arr = (uint8_t*)&res;
But here are two caveats in this:
1) I is a strict aliasing rule violation (you can somehow to work around it by replacing uint8_t with char)
2) The order of the returned bytes will be implementation specific (endianness dependent) and thus not portable. Also note that the result is unsigned long long, so you might get extra padding zeros as either the beginning of the array or in the end of it.
Any size string in the chosen order. Portable digit conversion and it optimizes very well on the ASCII systems. https://godbolt.org/g/Ycah1e
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int CharToDigit(const char c);
void *StringToTable(const char *str, const void *buff, const int order)
{
uint8_t *ptr = (uint8_t *)buff;
size_t len;
int incr = order ? 1 : -1;
if(buff && str)
{
len = strlen(str);
if(len &1) return NULL;
ptr += order ? 0 : len / 2 - 1;
while(*str)
{
int d1 = CharToDigit(*str++);
int d2 = CharToDigit(*str++);
if(d1 == -1 || d2 == -1) return NULL;
*ptr = d1 * 16 + d2;
ptr += incr;
}
}
return buff;
}
int main(void) {
int index = 0;
char *str = "78deAc8912fF0f3B";
uint8_t buff[strlen(str) / 2];
StringToTable(str, buff, 0);
printf("String: %s\nResult: ", str);
for(index = 0; index < strlen(str) / 2; index++ )
{
printf("[0x%02hhx]", buff[index] );
}
printf("\n");
StringToTable(str, buff, 1);
printf("String: %s\nResult: ", str);
for(index = 0; index < strlen(str) / 2; index++ )
{
printf("[0x%02hhx]", buff[index] );
}
printf("\n");
return 0;
}
int CharToDigit(const char c)
{
switch(c)
{
case 'a':
case 'A':
return 10;
case 'b':
case 'B':
return 11;
case 'c':
case 'C':
return 12;
case 'd':
case 'D':
return 13;
case 'e':
case 'E':
return 14;
case 'f':
case 'F':
return 15;
case '0':
return 0;
case '1':
return 1;
case '2':
return 2;
case '3':
return 3;
case '4':
return 4;
case '5':
return 5;
case '6':
return 6;
case '7':
return 7;
case '8':
return 8;
case '9':
return 9;
default:
return -1;
}
}
You can conver a character to an int like so
static inline int char2int(char Ch)
{
return(Ch>='0'&&Ch<='9')?(Ch-'0'):(Ch-'A'+10);
//assuming correct input with no lowercase letters
}
Two characters then with
static inline
int chars2int(unsigned char const Chars[2])
{
return (char2int(Chars[0])<<4)|(char2int(Chars[1]));
}
And several characters by converting each pair:
static inline int char2int(char Ch)
{
return(Ch>='0'&&Ch<='9')?(Ch-'0'):(Ch-'A'+10);
}
static inline
int chars2int(unsigned char const Chars[2])
{
return (char2int(Chars[0])<<4)|(char2int(Chars[1]));
}
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
char const inp[] = "DABC95C1";
assert((sizeof(inp)-1)%2==0);
unsigned i;
unsigned char out[(sizeof(inp)-1)/2];
for(i=0;i<sizeof(inp);i+=2){
out[i/2]=chars2int((unsigned char*)inp+i);
}
for(i=0;i<sizeof(out);i++)
printf("%2x\n", out[i]);
}

how to check if the input is a number or not in C?

In the main function of C:
void main(int argc, char **argv)
{
// do something here
}
In the command line, we will type any number for example 1 or 2 as input, but it will be treated as char array for the parameter of argv, but how to make sure the input is a number, in case people typed hello or c?
Another way of doing it is by using isdigit function. Below is the code for it:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define MAXINPUT 100
int main()
{
char input[MAXINPUT] = "";
int length,i;
scanf ("%s", input);
length = strlen (input);
for (i=0;i<length; i++)
if (!isdigit(input[i]))
{
printf ("Entered input is not a number\n");
exit(1);
}
printf ("Given input is a number\n");
}
You can use a function like strtol() which will convert a character array to a long.
It has a parameter which is a way to detect the first character that didn't convert properly. If this is anything other than the end of the string, then you have a problem.
See the following program for an example:
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char *argv[]) {
int i;
long val;
char *next;
// Process each argument given.
for (i = 1; i < argc; i++) {
// Get value with failure detection.
val = strtol (argv[i], &next, 10);
// Check for empty string and characters left after conversion.
if ((next == argv[i]) || (*next != '\0')) {
printf ("'%s' is not valid\n", argv[i]);
} else {
printf ("'%s' gives %ld\n", argv[i], val);
}
}
return 0;
}
Running this, you can see it in operation:
pax> testprog hello "" 42 12.2 77x
'hello' is not valid
'' is not valid
'42' gives 42
'12.2' is not valid
'77x' is not valid
Using scanf is very easy, this is an example :
if (scanf("%d", &val_a_tester) == 1) {
... // it's an integer
}
A self-made solution:
bool isNumeric(const char *str)
{
while(*str != '\0')
{
if(*str < '0' || *str > '9')
return false;
str++;
}
return true;
}
Note that this solution should not be used in production-code, because it has severe limitations. But I like it for understanding C-Strings and ASCII.
Using fairly simple code:
int i;
int value;
int n;
char ch;
/* Skip i==0 because that will be the program name */
for (i=1; i<argc; i++) {
n = sscanf(argv[i], "%d%c", &value, &ch);
if (n != 1) {
/* sscanf didn't find a number to convert, so it wasn't a number */
}
else {
/* It was */
}
}
I was struggling with this for awhile, so I thought I'd just add my two cents:
1) Create a separate function to check if an fgets input consists entirely of numbers:
int integerCheck(){
char myInput[4];
fgets(myInput, sizeof(myInput), stdin);
int counter = 0;
int i;
for (i=0; myInput[i]!= '\0'; i++){
if (isalpha(myInput[i]) != 0){
counter++;
if(counter > 0){
printf("Input error: Please try again. \n ");
return main();
}
}
}
return atoi(myInput);
}
The above starts a loop through every unit of an fgets input until the ending NULL value. If it comes across a letter or an operator, it adds "1" to the int "counter" which is initially set to 0. Once the counter becomes greater than 0, the nested if statement instructs the loop to print an error message & then restart the program. When the loops completes, if int 'counter' is still the value of 0, it returns the initially inputted integer to be used in the main function ...
2) the main function would be:
int main(void){
unsigned int numberOne;
unsigned int numberTwo;
numberOne = integerCheck();
numberTwo = integerCheck();
return numberOne*numberTwo;
}
Assuming both integers are inputted correctly, the example provided will yield the result of int "numberOne" multiplied by int "numberTwo". The program will repeat for however long it takes to get two properly inputted integers.
if (sscanf(command_level[2], "%f%c", &check_f, &check_c)!=1)
{
is_num=false;
}
else
{
is_num=true;
}
if(sscanf(command_level[2],"%f",&check_f) != 1)
{
is_num=false;
}
how about this?
This works for me
#include <string.h>
int isNumber(char *n) {
int i = strlen(n);
int isnum = (i>0);
while (i-- && isnum) {
if (!(n[i] >= '0' && n[i] <= '9')) {
isnum = 0;
}
}
return isnum;
}
e.g.:
printf("%i\n", isNumber("12")); // 1
printf("%i\n", isNumber("033")); // 1
printf("%i\n", isNumber("0")); // 1
printf("%i\n", isNumber("")); // 0
printf("%i\n", isNumber("aaa")); // 0
printf("%i\n", isNumber("\n")); // 0
printf("%i\n", isNumber("a0\n")); // 0
The C library function int isdigit(int c) checks if the passed character is a decimal digit character.
#include <stdio.h>
#include <ctype.h>
int main () {
int var1 = 'h';
int var2 = '2';
if( isdigit(var1) ) {
printf("var1 = |%c| is a digit\n", var1 );
} else {
printf("var1 = |%c| is not a digit\n", var1 );
}
if( isdigit(var2) ) {
printf("var2 = |%c| is a digit\n", var2 );
} else {
printf("var2 = |%c| is not a digit\n", var2 );
}
return(0);
}
the result is :
var1 = |h| is not a digit
var2 = |2| is a digit
The sscanf() solution is better in terms of code lines. My answer here is a user-build function that does almost the same as sscanf(). Stores the converted number in a pointer and returns a value called "val". If val comes out as zero, then the input is in unsupported format, hence conversion failed. Hence, use the pointer value only when val is non-zero.
It works only if the input is in base-10 form.
#include <stdio.h>
#include <string.h>
int CONVERT_3(double* Amt){
char number[100];
// Input the Data
printf("\nPlease enter the amount (integer only)...");
fgets(number,sizeof(number),stdin);
// Detection-Conversion begins
int iters = strlen(number)-2;
int val = 1;
int pos;
double Amount = 0;
*Amt = 0;
for(int i = 0 ; i <= iters ; i++ ){
switch(i){
case 0:
if(number[i]=='+'){break;}
if(number[i]=='-'){val = 2; break;}
if(number[i]=='.'){val = val + 10; pos = 0; break;}
if(number[i]=='0'){Amount = 0; break;}
if(number[i]=='1'){Amount = 1; break;}
if(number[i]=='2'){Amount = 2; break;}
if(number[i]=='3'){Amount = 3; break;}
if(number[i]=='4'){Amount = 4; break;}
if(number[i]=='5'){Amount = 5; break;}
if(number[i]=='6'){Amount = 6; break;}
if(number[i]=='7'){Amount = 7; break;}
if(number[i]=='8'){Amount = 8; break;}
if(number[i]=='9'){Amount = 9; break;}
default:
switch(number[i]){
case '.':
val = val + 10;
pos = i;
break;
case '0':
Amount = (Amount)*10;
break;
case '1':
Amount = (Amount)*10 + 1;
break;
case '2':
Amount = (Amount)*10 + 2;
break;
case '3':
Amount = (Amount)*10 + 3;
break;
case '4':
Amount = (Amount)*10 + 4;
break;
case '5':
Amount = (Amount)*10 + 5;
break;
case '6':
Amount = (Amount)*10 + 6;
break;
case '7':
Amount = (Amount)*10 + 7;
break;
case '8':
Amount = (Amount)*10 + 8;
break;
case '9':
Amount = (Amount)*10 + 9;
break;
default:
val = 0;
}
}
if( (!val) | (val>20) ){val = 0; break;}// val == 0
}
if(val==1){*Amt = Amount;}
if(val==2){*Amt = 0 - Amount;}
if(val==11){
int exp = iters - pos;
long den = 1;
for( ; exp-- ; ){
den = den*10;
}
*Amt = Amount/den;
}
if(val==12){
int exp = iters - pos;
long den = 1;
for( ; exp-- ; ){
den = den*10;
}
*Amt = 0 - (Amount/den);
}
return val;
}
int main(void) {
double AM = 0;
int c = CONVERT_3(&AM);
printf("\n\n%d %lf\n",c,AM);
return(0);
}

Resources