The truth is rarely pure and never simple

Zeichenfolge in Ganzzahl konvertieren

Eine ziemlich häufiges Problem bei der Anwendungsentwicklung ist die Umwandlung von Zeichenfolgen in eine Ganzzahl zur Weiterverarbeitung. Bei C oder C++ ist atoi() der einfachste Weg, um vom char array zum int zu kommen. Leider ermöglicht atoi() keine präzise Fehlererkennung, weil eine ungültige Eingabe ebenso wie die korrekte Eingabe “0” zum Ergebnis “0” führt:

$ cat atoi.c 
#include <stdio.h>
#include <stdlib.h>
int main() { 
  printf("%i - %i ", atoi("invalid"), atoi("0")); 
  return 0; 
} 

$ gcc -Wall -pedantic atoi.c -o atoi 
$ ./atoi 0 - 0

Meist wird empfohlen, strtol() zu nutzen. Diese Funktion ist mächtiger und v.a. brauchbarer bei der Fehlererkennung. Aber wieviel Performance kostet strtol() im Vergleich zu atoi()? Ein Shell-Script

#!/bin/bash

function instr_by_cmd()
{
	valgrind --tool=callgrind --callgrind-out-file=/dev/null "$1" 2>&1 | grep refs | sed 's/^.*\s//;s/,//'
}
function print_head()
{
	echo -e "\nTeste $1()";
	printf "%15s | %15s | %15s\n" Test Instruktionen Ergebnis
	echo "----------------+-----------------+----------------"
}

# usage: test_source "headers" "template" "replacement"
function test_source()
{
	rm /tmp/source.c 2>/dev/null
	for CASE in "${TESTCASES[@]}"
	do
		for HEADER in $1
		do
        		echo "#include <$HEADER>" >> /tmp/source.c
		done
		echo "${2/CASE/$CASE}" >> /tmp/source.c
		gcc /tmp/source.c -o /tmp/source 2>/dev/null
		INSTR=$(instr_by_cmd "/tmp/source")
		RES=$(/tmp/source);
		sed -i 's/'$3'/'$RES'/' /tmp/source.c
		gcc /tmp/source.c -o /tmp/source 2>/dev/null
		INSTR=$(($INSTR-$(instr_by_cmd "/tmp/source")))
		printf "%15s | %15s | %15s\n" ">$CASE<" $INSTR $RES
		rm /tmp/source.c 
	done
	rm atoi 2>/dev/null
}

TESTCASES=( "4" "42" "4\n2" "424242" " 42 " "answer: 42" "2147483648" "-2147483649" "\n42\n" )

print_head atoi
test_source "stdlib.h stdio.h" "int main(){char * rem=0;char * input=\"CASE\";printf(\"%i\n\", atoi(input));return 0;}" "atoi(input)"
print_head strtol
test_source "stdlib.h stdio.h" "int main(){char * rem=0;char * input=\"CASE\";printf(\"%ld\n\", strtol(input,&rem,10));return 0;}" "strtol(input,&rem,10)"

bringt zusammen mit valgrind auf der Testplattform (Core 2 Duo E6600) folgende Ergebnisse für atoi()

Test Instruktionen Ergebnis
>4< 883 4
>42< 903 42
>4\n2< 894 4
>424242< 983 424242
> 42 < 920 42
>answer: 42< 867 0
>2147483648< 1063 -2147483648
>-21474836489< 1064 2147483647
>\n42\n 920 42

und für strtol()

Test Instruktionen Ergebnis
>4< 921 4
>42< 941 42
>4\n2< 932 4
>424242< 1021 424242
> 42 < 958 42
>answer: 42< 912 0
>2147483648< 1101 2147483648
>-21474836489< 1102 -2147483649
>\n42\n 958 42